Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-Flask-Security-Too for
openSUSE:Factory checked in at 2021-07-08 22:49:16
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-Flask-Security-Too (Old)
and /work/SRC/openSUSE:Factory/.python-Flask-Security-Too.new.2625 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-Flask-Security-Too"
Thu Jul 8 22:49:16 2021 rev:6 rq:904704 version:3.4.5
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-Flask-Security-Too/python-Flask-Security-Too.changes
2020-07-10 14:13:08.851581897 +0200
+++
/work/SRC/openSUSE:Factory/.python-Flask-Security-Too.new.2625/python-Flask-Security-Too.changes
2021-07-08 22:49:35.347927275 +0200
@@ -1,0 +2,60 @@
+Tue Jun 15 16:37:41 UTC 2021 - Antonio Larrosa <[email protected]>
+
+- Update to 3.4.5
+ * Security Vulnerability Fix. Two CSRF vulnerabilities were
+ reported: qrcode and login. This release fixes the more severe
+ of the 2 - the /login vulnerability. The QRcode issue has a
+ much smaller risk profile since a) it is only for two-factor
+ authentication using an authenticator app b) the qrcode is only
+ available during the time the user is first setting up their
+ authentication app. The QRcode issue has been fixed in 4.0.
+ * Fixed
+ - GET on /login and /change could return the callers
+ authentication_token. This is a security concern since GETs
+ don't have CSRF protection. This bug was introduced in 3.3.0.
+ * Backwards Compatibility Concerns. Fix CSRF vulnerability on
+ /login and /change that could return the callers authentication
+ token. Now, callers can only get the authentication token on
+ successful POST calls.
+
+- Update to 3.4.4
+ * Fix 3 regressions and a couple other bugs
+ * Fixed
+ - Basic Auth broken. When the unauthenticated handler was
+ changed to provide a more uniform/consistent response - it
+ broke using Basic Auth from a browser, since it always
+ redirected rather than returning 401. Now, if the response
+ headers contain WWW-Authenticate (which is set if basic
+ @auth_required method is used), a 401 is returned. See below
+ for backwards compatibility concerns.
+ - As part of figuring out issue 359 - a redirect loop was
+ found. In release 3.3.0 code was put in to redirect to
+ :py:data:`SECURITY_POST_LOGIN_VIEW` when GET or POST was
+ called and the caller was already authenticated. The method
+ used would honor the request next query parameter. This could
+ cause redirect loops. The pre-3.3.0 behavior of redirecting
+ to :py:data:`SECURITY_POST_LOGIN_VIEW` and ignoring the next
+ parameter has been restored.
+ - Fix peewee. Turns out - due to lack of unit tests - peewee
+ hasn't worked since 'permissions' were added in 3.3.
+ Furthermore, changes in 3.4 around get_id and alternative
+ tokens also didn't work since peewee defines its own get_id
+ method.
+ * Compatibility Concerns. In 3.3.0, flask_security.auth_required
+ was changed to add a default argument if none was given. The
+ default include all current methods - session, token, and
+ basic. However basic really isn't like the others and requires
+ that we send back a WWW-Authenticate header if authentication
+ fails (and return a 401 and not redirect). basic has been
+ removed from the default set and must once again be explicitly
+ requested.
+- Rebase patch to remove another case where mongo is used:
+ * no-mongodb.patch
+- Rebase patch to fix context:
+ * fix-dependencies.patch
+- Add patch to fix failed tests (so an exception is not
+ raised if phone.data is None). Submitted upstream at
+ gh#Flask-Middleware/flask-security#495:
+ * 0001-Do-not-raise-a-TypeError-exception-if-phone.data-is-.patch
+
+-------------------------------------------------------------------
Old:
----
Flask-Security-Too-3.4.3.tar.gz
New:
----
0001-Do-not-raise-a-TypeError-exception-if-phone.data-is-.patch
Flask-Security-Too-3.4.5.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-Flask-Security-Too.spec ++++++
--- /var/tmp/diff_new_pack.YlLHAO/_old 2021-07-08 22:49:35.911922923 +0200
+++ /var/tmp/diff_new_pack.YlLHAO/_new 2021-07-08 22:49:35.915922892 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-Flask-Security-Too
#
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2021 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
%define skip_python2 1
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-Flask-Security-Too
-Version: 3.4.3
+Version: 3.4.5
Release: 0
Summary: Security for Flask apps
License: MIT
@@ -28,6 +28,7 @@
Patch0: no-mongodb.patch
Patch1: no-setup-dependencies.patch
Patch2: fix-dependencies.patch
+Patch3: 0001-Do-not-raise-a-TypeError-exception-if-phone.data-is-.patch
BuildRequires: %{python_module Babel >= 1.3}
BuildRequires: %{python_module Flask >= 1.0.2}
BuildRequires: %{python_module Flask-BabelEx >= 0.9.3}
++++++ 0001-Do-not-raise-a-TypeError-exception-if-phone.data-is-.patch ++++++
>From fc94ad58537d83b1f5500876da4a3026654645ba Mon Sep 17 00:00:00 2001
From: Antonio Larrosa <[email protected]>
Date: Tue, 15 Jun 2021 19:36:50 +0200
Subject: [PATCH] Do not raise a TypeError exception if phone.data is None
Running the tests on the openSUSE build service to generate
packages fails because a TypeError exception is raised.
```
TypeError: object of type 'NoneType' has no len()
```
This commit checks that phone.data is not None before calling
len() in the two lines where the exception is raised.
---
flask_security/forms.py | 3 ++-
flask_security/views.py | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/flask_security/forms.py b/flask_security/forms.py
index c793a99..83665fa 100644
--- a/flask_security/forms.py
+++ b/flask_security/forms.py
@@ -593,7 +593,8 @@ class TwoFactorSetupForm(Form, UserEmailFormMixin):
self.setup.errors = list()
self.setup.errors.append(get_message("TWO_FACTOR_METHOD_NOT_AVAILABLE")[0])
return False
- if self.setup.data == "sms" and len(self.phone.data) > 0:
+ if (self.setup.data == "sms" and
+ self.phone.data and len(self.phone.data) > 0):
# Somewhat bizarre - but this isn't required the first time around
# when they select "sms". Then they get a field to fill out with
# phone number, then Submit again.
diff --git a/flask_security/views.py b/flask_security/views.py
index c33a016..3aaca95 100644
--- a/flask_security/views.py
+++ b/flask_security/views.py
@@ -751,7 +751,8 @@ def two_factor_setup():
session["tf_primary_method"] = pm
session["tf_state"] = "validating_profile"
- new_phone = form.phone.data if len(form.phone.data) > 0 else None
+ new_phone = form.phone.data if (form.phone.data and
+ len(form.phone.data) > 0) else None
if new_phone:
user.tf_phone_number = new_phone
_datastore.put(user)
--
2.31.1
++++++ Flask-Security-Too-3.4.3.tar.gz -> Flask-Security-Too-3.4.5.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/CHANGES.rst
new/Flask-Security-Too-3.4.5/CHANGES.rst
--- old/Flask-Security-Too-3.4.3/CHANGES.rst 2020-06-13 18:53:19.000000000
+0200
+++ new/Flask-Security-Too-3.4.5/CHANGES.rst 2021-01-08 21:02:54.000000000
+0100
@@ -14,10 +14,74 @@
.. _here: https://github.com/Flask-Middleware/flask-security/issues/85
+Version 3.4.5
+--------------
+
+Release January x, 2021
+
+Security Vulnerability Fix.
+
+Two CSRF vulnerabilities were reported: `qrcode`_ and `login`_. This release
+fixes the more severe of the 2 - the /login vulnerability. The QRcode issue
+has a much smaller risk profile since a) it is only for two-factor
authentication
+using an authenticator app b) the qrcode is only available during the time
+the user is first setting up their authentication app.
+The QRcode issue has been fixed in 4.0.
+
+.. _qrcode: https://github.com/Flask-Middleware/flask-security/issues/418
+.. _login: https://github.com/Flask-Middleware/flask-security/issues/421
+
+Fixed
++++++
+
+- (:issue:`421`) GET on /login and /change could return the callers
authentication_token. This is a security
+ concern since GETs don't have CSRF protection. This bug was introduced in
3.3.0.
+
+Backwards Compatibility Concerns
+++++++++++++++++++++++++++++++++
+
+- (:issue:`421`) Fix CSRF vulnerability on /login and /change that could
return the callers authentication token.
+ Now, callers can only get the authentication token on successful POST calls.
+
+
+Version 3.4.4
+--------------
+
+Released July 26, 2020
+
+Bug/regression fixes.
+
+Fixed
++++++
+
+- (:issue:`359`) Basic Auth broken. When the unauthenticated handler was
changed to provide a more
+ uniform/consistent response - it broke using Basic Auth from a browser,
since it always redirected rather than
+ returning 401. Now, if the response headers contain ``WWW-Authenticate``
+ (which is set if ``basic`` @auth_required method is used), a 401 is
returned. See below
+ for backwards compatibility concerns.
+
+- (:pr:`362`) As part of figuring out issue 359 - a redirect loop was found.
In release 3.3.0 code was put
+ in to redirect to :py:data:`SECURITY_POST_LOGIN_VIEW` when GET or POST was
called and the caller was already authenticated. The
+ method used would honor the request ``next`` query parameter. This could
cause redirect loops. The pre-3.3.0 behavior
+ of redirecting to :py:data:`SECURITY_POST_LOGIN_VIEW` and ignoring the
``next`` parameter has been restored.
+
+- (:issue:`347`) Fix peewee. Turns out - due to lack of unit tests - peewee
hasn't worked since 'permissions' were added in 3.3.
+ Furthermore, changes in 3.4 around get_id and alternative tokens also didn't
work since peewee defines its own get_id.
+
+- (:pr:`xx`) Backport the reset_access CLI command from 4.0 - this is really
useful for administrators.
+
+Compatibility Concerns
+++++++++++++++++++++++
+
+In 3.3.0, :func:`.auth_required` was changed to add a default argument if none
was given. The default
+include all current methods - ``session``, ``token``, and ``basic``. However
``basic`` really isn't like the others
+and requires that we send back a ``WWW-Authenticate`` header if authentication
fails (and return a 401 and not redirect).
+``basic`` has been removed from the default set and must once again be
explicitly requested.
+
Version 3.4.3
-------------
-Released June 12, 2020
+Released June 14, 2020
Minor fixes for a regression and a couple other minor changes
@@ -25,7 +89,7 @@
+++++
- (:issue:`340`) Fix regression where tf_phone_number was required, even if
SMS wasn't configured.
-- (:pr:`xx`) Pick up some small documentation fixes from 4.0.0.
+- (:pr:`342`) Pick up some small documentation fixes from 4.0.0.
Version 3.4.2
-------------
@@ -141,7 +205,8 @@
this couldn't possibly work with CSRF.
The old behavior has been restored, with the subtle change that older
Flask-Security
releases did not look at "next" in the form or request for the redirect,
-and now, all redirects from the login view will honor "next".
+and now, all redirects from the login view will honor "next" (N.B. see 3.4.4 -
the
+handling of "next" has been removed due to redirect loops).
Version 3.3.1
-------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/Flask-Security-Too-3.4.3/Flask_Security_Too.egg-info/PKG-INFO
new/Flask-Security-Too-3.4.5/Flask_Security_Too.egg-info/PKG-INFO
--- old/Flask-Security-Too-3.4.3/Flask_Security_Too.egg-info/PKG-INFO
2020-06-13 19:01:06.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/Flask_Security_Too.egg-info/PKG-INFO
2021-01-08 21:10:44.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: Flask-Security-Too
-Version: 3.4.3
+Version: 3.4.5
Summary: Simple security for Flask apps.
Home-page: https://github.com/Flask-Middleware/flask-security
Author: Matt Wright & Chris Wagner
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/Flask-Security-Too-3.4.3/Flask_Security_Too.egg-info/requires.txt
new/Flask-Security-Too-3.4.5/Flask_Security_Too.egg-info/requires.txt
--- old/Flask-Security-Too-3.4.3/Flask_Security_Too.egg-info/requires.txt
2020-06-13 19:01:06.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/Flask_Security_Too.egg-info/requires.txt
2021-01-08 21:10:44.000000000 +0100
@@ -12,7 +12,7 @@
Pallets-Sphinx-Themes>=1.2.0
Sphinx>=1.8.5
sphinx-issues>=1.2.0
-Flask-Mongoengine>=0.9.5
+Flask-Mongoengine~=0.9.5
peewee>=3.11.2
Flask-SQLAlchemy>=2.3
argon2_cffi>=19.1.0
@@ -23,8 +23,8 @@
cryptography>=2.3.1
isort>=4.2.2
mock>=1.3.0
-mongoengine>=0.15.3
-mongomock>=3.14.0
+mongoengine~=0.19.1
+mongomock~=3.19.0
msgcheck>=2.9
pony>=0.7.11
phonenumberslite>=8.11.1
@@ -32,6 +32,7 @@
pydocstyle>=1.0.0
pymysql>=0.9.3
pyqrcode>=1.2
+pytest==4.6.11
pytest-black>=0.3.8
pytest-cache>=1.0
pytest-cov>=2.5.1
@@ -45,7 +46,7 @@
Pallets-Sphinx-Themes>=1.2.0
Sphinx>=1.8.5
sphinx-issues>=1.2.0
-Flask-Mongoengine>=0.9.5
+Flask-Mongoengine~=0.9.5
peewee>=3.11.2
Flask-SQLAlchemy>=2.3
argon2_cffi>=19.1.0
@@ -56,8 +57,8 @@
cryptography>=2.3.1
isort>=4.2.2
mock>=1.3.0
-mongoengine>=0.15.3
-mongomock>=3.14.0
+mongoengine~=0.19.1
+mongomock~=3.19.0
msgcheck>=2.9
pony>=0.7.11
phonenumberslite>=8.11.1
@@ -65,6 +66,7 @@
pydocstyle>=1.0.0
pymysql>=0.9.3
pyqrcode>=1.2
+pytest==4.6.11
pytest-black>=0.3.8
pytest-cache>=1.0
pytest-cov>=2.5.1
@@ -82,7 +84,7 @@
sphinx-issues>=1.2.0
[tests]
-Flask-Mongoengine>=0.9.5
+Flask-Mongoengine~=0.9.5
peewee>=3.11.2
Flask-SQLAlchemy>=2.3
argon2_cffi>=19.1.0
@@ -93,8 +95,8 @@
cryptography>=2.3.1
isort>=4.2.2
mock>=1.3.0
-mongoengine>=0.15.3
-mongomock>=3.14.0
+mongoengine~=0.19.1
+mongomock~=3.19.0
msgcheck>=2.9
pony>=0.7.11
phonenumberslite>=8.11.1
@@ -102,6 +104,7 @@
pydocstyle>=1.0.0
pymysql>=0.9.3
pyqrcode>=1.2
+pytest==4.6.11
pytest-black>=0.3.8
pytest-cache>=1.0
pytest-cov>=2.5.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/PKG-INFO
new/Flask-Security-Too-3.4.5/PKG-INFO
--- old/Flask-Security-Too-3.4.3/PKG-INFO 2020-06-13 19:01:06.000000000
+0200
+++ new/Flask-Security-Too-3.4.5/PKG-INFO 2021-01-08 21:10:44.000000000
+0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: Flask-Security-Too
-Version: 3.4.3
+Version: 3.4.5
Summary: Simple security for Flask apps.
Home-page: https://github.com/Flask-Middleware/flask-security
Author: Matt Wright & Chris Wagner
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/docs/conf.py
new/Flask-Security-Too-3.4.5/docs/conf.py
--- old/Flask-Security-Too-3.4.3/docs/conf.py 2020-06-13 18:53:19.000000000
+0200
+++ new/Flask-Security-Too-3.4.5/docs/conf.py 2021-01-08 21:02:54.000000000
+0100
@@ -58,7 +58,7 @@
# built documents.
#
# The short X.Y version.
-version = "3.4.3"
+version = "3.4.5"
# The full version, including alpha/beta/rc tags.
release = version
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/docs/customizing.rst
new/Flask-Security-Too-3.4.5/docs/customizing.rst
--- old/Flask-Security-Too-3.4.3/docs/customizing.rst 2020-06-13
18:53:19.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/docs/customizing.rst 2021-01-08
21:02:54.000000000 +0100
@@ -196,7 +196,7 @@
from flask_mail import Message
-
+
# Setup the task
@celery.task
def send_flask_mail(**kwargs):
@@ -326,19 +326,23 @@
401, 403, Oh My
+++++++++++++++
For a very long read and discussion; look at `this`_. Out of the box,
Flask-Security in
-tandem with Flask-Login, behaves as follows:
+tandem with Flask-Login, behave as follows:
- * If authentication fails as the result of a `@login_required`,
`@auth_required`,
- `@http_auth_required`, or `@token_auth_required` then if the request
'wants' a JSON
+ * If authentication fails as the result of a `@login_required`,
`@auth_required("session", "token")`,
+ or `@token_auth_required` then if the request 'wants' a JSON
response, :meth:`.Security.render_json` is called with a 401 status
code. If not
then flask_login.LoginManager.unauthorized() is called. By default THAT
will redirect to
a login view.
+ * If authentication fails as the result of a `@http_auth_required` or
`@auth_required("basic")`
+ then a 401 is returned along with the http header ``WWW-Authenticate``
set to
+ ``Basic realm="xxxx"``. The realm name is defined by
:py:data:`SECURITY_DEFAULT_HTTP_AUTH_REALM`.
+
* If authorization fails as the result of `@roles_required`,
`@roles_accepted`,
`@permissions_required`, or `@permissions_accepted`, then if the request
'wants' a JSON
response, :meth:`.Security.render_json` is called with a 403 status
code. If not,
- then if *SECURITY_UNAUTHORIZED_VIEW* is defined, the response will
redirected.
- If *SECURITY_UNAUTHORIZED_VIEW* is not defined, then ``abort(403)`` is
called.
+ then if :py:data:`SECURITY_UNAUTHORIZED_VIEW` is defined, the response
will redirected.
+ If :py:data:`SECURITY_UNAUTHORIZED_VIEW` is not defined, then
``abort(403)`` is called.
All this can be easily changed by registering any or all of
:meth:`.Security.render_json`,
:meth:`.Security.unauthn_handler` and :meth:`.Security.unauthz_handler`.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/docs/quickstart.rst
new/Flask-Security-Too-3.4.5/docs/quickstart.rst
--- old/Flask-Security-Too-3.4.3/docs/quickstart.rst 2020-06-13
18:53:19.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/docs/quickstart.rst 2021-01-08
21:02:54.000000000 +0100
@@ -273,6 +273,7 @@
class Role(db.Document, RoleMixin):
name = db.StringField(max_length=80, unique=True)
description = db.StringField(max_length=255)
+ permissions = db.StringField(max_length=255)
class User(db.Document, UserMixin):
email = db.StringField(max_length=255)
@@ -348,15 +349,18 @@
# Create database connection object
db = FlaskDB(app)
- class Role(db.Model, RoleMixin):
+ class Role(RoleMixin, db.Model):
name = CharField(unique=True)
description = TextField(null=True)
+ permissions = TextField(null=True)
- class User(db.Model, UserMixin):
+ # N.B. order is important since db.Model also contains a get_id() -
+ # we need the one from UserMixin.
+ class User(UserMixin, db.Model):
email = TextField()
password = TextField()
active = BooleanField(default=True)
- fs_uniquifier = TextField()
+ fs_uniquifier = TextField(unique=True, null=False)
confirmed_at = DateTimeField(null=True)
class UserRoles(db.Model):
@@ -368,6 +372,9 @@
name = property(lambda self: self.role.name)
description = property(lambda self: self.role.description)
+ def get_permissions(self):
+ return self.role.get_permissions()
+
# Setup Flask-Security
user_datastore = PeeweeUserDatastore(db, User, Role, UserRoles)
security = Security(app, user_datastore)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/flask_security/__init__.py
new/Flask-Security-Too-3.4.5/flask_security/__init__.py
--- old/Flask-Security-Too-3.4.3/flask_security/__init__.py 2020-06-13
18:53:19.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/flask_security/__init__.py 2021-01-08
21:02:54.000000000 +0100
@@ -101,4 +101,4 @@
verify_and_update_password,
)
-__version__ = "3.4.3"
+__version__ = "3.4.5"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/flask_security/cli.py
new/Flask-Security-Too-3.4.5/flask_security/cli.py
--- old/Flask-Security-Too-3.4.3/flask_security/cli.py 2020-06-13
18:53:19.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/flask_security/cli.py 2021-01-08
21:02:54.000000000 +0100
@@ -121,7 +121,7 @@
@with_appcontext
@commit
def roles_add(user, role):
- """Add user to role."""
+ """Add role to user."""
user, role = _datastore._prepare_role_modify_args(user, role)
if user is None:
raise click.UsageError("Cannot find user.")
@@ -129,7 +129,7 @@
raise click.UsageError("Cannot find role.")
if _datastore.add_role_to_user(user, role):
click.secho(
- 'Role "{0}" added to user "{1}" ' "successfully.".format(role,
user),
+ 'Role "{0}" added to user "{1}" '
"successfully.".format(role.name, user),
fg="green",
)
else:
@@ -142,7 +142,7 @@
@with_appcontext
@commit
def roles_remove(user, role):
- """Remove user from role."""
+ """Remove role from user."""
user, role = _datastore._prepare_role_modify_args(user, role)
if user is None:
raise click.UsageError("Cannot find user.")
@@ -150,7 +150,8 @@
raise click.UsageError("Cannot find role.")
if _datastore.remove_role_from_user(user, role):
click.secho(
- 'Role "{0}" removed from user "{1}" ' "successfully.".format(role,
user),
+ 'Role "{0}" removed from user "{1}" '
+ "successfully.".format(role.name, user),
fg="green",
)
else:
@@ -165,7 +166,7 @@
"""Activate a user."""
user_obj = _datastore.get_user(user)
if user_obj is None:
- raise click.UsageError("ERROR: User not found.")
+ raise click.UsageError("User not found.")
if _datastore.activate_user(user_obj):
click.secho('User "{0}" has been activated.'.format(user), fg="green")
else:
@@ -180,8 +181,29 @@
"""Deactivate a user."""
user_obj = _datastore.get_user(user)
if user_obj is None:
- raise click.UsageError("ERROR: User not found.")
+ raise click.UsageError("User not found.")
if _datastore.deactivate_user(user_obj):
click.secho('User "{0}" has been deactivated.'.format(user),
fg="green")
else:
click.secho('User "{0}" was already deactivated.'.format(user),
fg="yellow")
+
+
[email protected](
+ "reset_access",
+ help="Reset all authentication credentials for user."
+ " This includes session, auth token, two-factor"
+ " and unified sign in secrets. ",
+)
[email protected]("user")
+@with_appcontext
+@commit
+def users_reset_access(user):
+ """ Reset all authentication tokens etc."""
+ user_obj = _datastore.get_user(user)
+ if user_obj is None:
+ raise click.UsageError("User not found.")
+ _datastore.reset_user_access(user_obj)
+ click.secho(
+ 'User "{user}" authentication credentials have been
reset.'.format(user=user),
+ fg="green",
+ )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/flask_security/core.py
new/Flask-Security-Too-3.4.5/flask_security/core.py
--- old/Flask-Security-Too-3.4.3/flask_security/core.py 2020-06-13
18:53:19.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/flask_security/core.py 2021-01-08
21:02:54.000000000 +0100
@@ -678,6 +678,9 @@
Caller must commit to DB.
.. versionadded:: 3.3.0
+
+ .. deprecated:: 3.4.4
+ Use :meth:`.UserDatastore.remove_permissions_from_role`
"""
if hasattr(self, "permissions"):
current_perms = self.get_permissions()
@@ -688,7 +691,7 @@
else:
perms = {permissions}
self.permissions = ",".join(current_perms.union(perms))
- else:
+ else: # pragma: no cover
raise NotImplementedError("Role model doesn't have permissions")
def remove_permissions(self, permissions):
@@ -700,6 +703,9 @@
Caller must commit to DB.
.. versionadded:: 3.3.0
+
+ .. deprecated:: 3.4.4
+ Use :meth:`.UserDatastore.remove_permissions_from_role`
"""
if hasattr(self, "permissions"):
current_perms = self.get_permissions()
@@ -710,7 +716,7 @@
else:
perms = {permissions}
self.permissions = ",".join(current_perms.difference(perms))
- else:
+ else: # pragma: no cover
raise NotImplementedError("Role model doesn't have permissions")
@@ -791,9 +797,8 @@
"""
for role in self.roles:
- if hasattr(role, "permissions"):
- if permission in role.get_permissions():
- return True
+ if permission in role.get_permissions():
+ return True
return False
def get_security_payload(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/flask_security/datastore.py
new/Flask-Security-Too-3.4.5/flask_security/datastore.py
--- old/Flask-Security-Too-3.4.3/flask_security/datastore.py 2020-06-13
18:53:19.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/flask_security/datastore.py 2021-01-08
21:02:54.000000000 +0100
@@ -202,6 +202,48 @@
self.put(user)
return rv
+ def add_permissions_to_role(self, role, permissions):
+ """Add one or more permissions to role.
+
+ :param role: The role to modify. Can be a Role object or
+ string role name
+ :param permissions: a set, list, or single string.
+ :return: True if permissions added, False if role doesn't exist.
+
+ Caller must commit to DB.
+
+ .. versionadded:: 3.4.4
+ """
+
+ rv = False
+ user, role = self._prepare_role_modify_args(None, role)
+ if role:
+ rv = True
+ role.add_permissions(permissions)
+ self.put(role)
+ return rv
+
+ def remove_permissions_from_role(self, role, permissions):
+ """Remove one or more permissions from a role.
+
+ :param role: The role to modify. Can be a Role object or
+ string role name
+ :param permissions: a set, list, or single string.
+ :return: True if permissions removed, False if role doesn't exist.
+
+ Caller must commit to DB.
+
+ .. versionadded:: 3.4.4
+ """
+
+ rv = False
+ user, role = self._prepare_role_modify_args(None, role)
+ if role:
+ rv = True
+ role.remove_permissions(permissions)
+ self.put(role)
+ return rv
+
def toggle_active(self, user):
"""Toggles a user's active status. Always returns True."""
user.active = not user.active
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/Flask-Security-Too-3.4.3/flask_security/decorators.py
new/Flask-Security-Too-3.4.5/flask_security/decorators.py
--- old/Flask-Security-Too-3.4.3/flask_security/decorators.py 2020-06-13
18:53:19.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/flask_security/decorators.py 2021-01-08
21:02:54.000000000 +0100
@@ -63,21 +63,29 @@
def default_unauthn_handler(mechanisms, headers=None):
""" Default callback for failures to authenticate
- If caller wants JSON - return 401
+ If caller wants JSON - return 401.
+ If caller wants BasicAuth - return 401 (the WWW-Authenticate header is
set).
Otherwise - assume caller is html and redirect if possible to a login view.
We let Flask-Login handle this.
"""
+ headers = headers or {}
msg = get_message("UNAUTHENTICATED")[0]
if config_value("BACKWARDS_COMPAT_UNAUTHN"):
return _get_unauthenticated_response(headers=headers)
if _security._want_json(request):
- # Ignore headers since today, the only thing in there might be
WWW-Authenticate
- # and we never want to send that in a JSON response (browsers will
intercept
- # that and pop up their own login form).
+ # Remove WWW-Authenticate from headers for JSON responses since
+ # browsers will intercept that and pop up their own login form.
+ headers.pop("WWW-Authenticate", None)
payload = json_error_response(errors=msg)
- return _security._render_json(payload, 401, None, None)
+ return _security._render_json(payload, 401, headers, None)
+
+ # Basic-Auth is often used to provide a browser based login form and then
the
+ # browser will always add the BasicAuth credentials. For that to work we
need to
+ # return 401 and not redirect to our login view.
+ if "WWW-Authenticate" in headers:
+ return Response(msg, 401, headers)
return _security.login_manager.unauthorized()
@@ -212,6 +220,9 @@
:param realm: optional realm name
+ If authentication fails, then a 401 with the 'WWW-Authenticate' header set
will be
+ returned.
+
Once authenticated, if so configured, CSRF protection will be tested.
"""
@@ -269,7 +280,7 @@
return 'Dashboard'
:param auth_methods: Specified mechanisms (token, basic, session). If not
specified
- then all current available mechanisms will be tried.
+ then all current available mechanisms (except "basic") will be tried.
:kwparam within: Add 'freshness' check to authentication. Is either an int
specifying # of minutes, or a callable that returns a timedelta. For
timedeltas,
timedelta.total_seconds() is used for the calculations:
@@ -297,6 +308,11 @@
On authentication failure `.Security.unauthorized_callback` (deprecated)
or :meth:`.Security.unauthn_handler` will be called.
+ .. note::
+ If "basic" is specified in addition to other methods, then if
authentication
+ fails, a 401 with the "WWW-Authenticate" header will be returned -
rather than
+ being redirected to the login view.
+
.. versionchanged:: 3.3.0
If ``auth_methods`` isn't specified, then all will be tried.
Authentication
mechanisms will always be tried in order of ``token``, ``session``,
``basic``
@@ -305,6 +321,9 @@
.. versionchanged:: 3.4.0
Added ``within`` and ``grace`` parameters to enforce a freshness check.
+ .. versionchanged:: 3.4.4
+ If ``auth_methods`` isn't specified try all mechanisms EXCEPT
``basic``.
+
"""
login_mechanisms = {
@@ -314,7 +333,7 @@
}
mechanisms_order = ["token", "session", "basic"]
if not auth_methods:
- auth_methods = {"basic", "session", "token"}
+ auth_methods = {"session", "token"}
else:
auth_methods = [am for am in auth_methods]
Binary files
old/Flask-Security-Too-3.4.3/flask_security/translations/ca_ES/LC_MESSAGES/flask_security.mo
and
new/Flask-Security-Too-3.4.5/flask_security/translations/ca_ES/LC_MESSAGES/flask_security.mo
differ
Binary files
old/Flask-Security-Too-3.4.3/flask_security/translations/da_DK/LC_MESSAGES/flask_security.mo
and
new/Flask-Security-Too-3.4.5/flask_security/translations/da_DK/LC_MESSAGES/flask_security.mo
differ
Binary files
old/Flask-Security-Too-3.4.3/flask_security/translations/de_DE/LC_MESSAGES/flask_security.mo
and
new/Flask-Security-Too-3.4.5/flask_security/translations/de_DE/LC_MESSAGES/flask_security.mo
differ
Binary files
old/Flask-Security-Too-3.4.3/flask_security/translations/es_ES/LC_MESSAGES/flask_security.mo
and
new/Flask-Security-Too-3.4.5/flask_security/translations/es_ES/LC_MESSAGES/flask_security.mo
differ
Binary files
old/Flask-Security-Too-3.4.3/flask_security/translations/fr_FR/LC_MESSAGES/flask_security.mo
and
new/Flask-Security-Too-3.4.5/flask_security/translations/fr_FR/LC_MESSAGES/flask_security.mo
differ
Binary files
old/Flask-Security-Too-3.4.3/flask_security/translations/ja_JP/LC_MESSAGES/flask_security.mo
and
new/Flask-Security-Too-3.4.5/flask_security/translations/ja_JP/LC_MESSAGES/flask_security.mo
differ
Binary files
old/Flask-Security-Too-3.4.3/flask_security/translations/nl_NL/LC_MESSAGES/flask_security.mo
and
new/Flask-Security-Too-3.4.5/flask_security/translations/nl_NL/LC_MESSAGES/flask_security.mo
differ
Binary files
old/Flask-Security-Too-3.4.3/flask_security/translations/pt_BR/LC_MESSAGES/flask_security.mo
and
new/Flask-Security-Too-3.4.5/flask_security/translations/pt_BR/LC_MESSAGES/flask_security.mo
differ
Binary files
old/Flask-Security-Too-3.4.3/flask_security/translations/pt_PT/LC_MESSAGES/flask_security.mo
and
new/Flask-Security-Too-3.4.5/flask_security/translations/pt_PT/LC_MESSAGES/flask_security.mo
differ
Binary files
old/Flask-Security-Too-3.4.3/flask_security/translations/ru_RU/LC_MESSAGES/flask_security.mo
and
new/Flask-Security-Too-3.4.5/flask_security/translations/ru_RU/LC_MESSAGES/flask_security.mo
differ
Binary files
old/Flask-Security-Too-3.4.3/flask_security/translations/tr_TR/LC_MESSAGES/flask_security.mo
and
new/Flask-Security-Too-3.4.5/flask_security/translations/tr_TR/LC_MESSAGES/flask_security.mo
differ
Binary files
old/Flask-Security-Too-3.4.3/flask_security/translations/zh_Hans_CN/LC_MESSAGES/flask_security.mo
and
new/Flask-Security-Too-3.4.5/flask_security/translations/zh_Hans_CN/LC_MESSAGES/flask_security.mo
differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/flask_security/views.py
new/Flask-Security-Too-3.4.5/flask_security/views.py
--- old/Flask-Security-Too-3.4.3/flask_security/views.py 2020-06-13
18:53:19.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/flask_security/views.py 2021-01-08
21:02:54.000000000 +0100
@@ -142,21 +142,20 @@
"""
if current_user.is_authenticated and request.method == "POST":
- # Just redirect current_user to POST_LOGIN_VIEW (or next).
+ # Just redirect current_user to POST_LOGIN_VIEW.
# While its tempting to try to logout the current user and login the
# new requested user - that simply doesn't work with CSRF.
- # While this is close to anonymous_user_required - it differs in that
- # it uses get_post_login_redirect which correctly handles 'next'.
- # TODO: consider changing anonymous_user_required to also call
- # get_post_login_redirect - not sure why it never has?
+ # This does NOT use get_post_login_redirect() so that it doesn't look
at
+ # 'next' - which can cause infinite redirect loops
+ # (see test_common::test_authenticated_loop)
if _security._want_json(request):
payload = json_error_response(
errors=get_message("ANONYMOUS_USER_REQUIRED")[0]
)
return _security._render_json(payload, 400, None, None)
else:
- return redirect(get_post_login_redirect())
+ return redirect(get_url(_security.post_login_view))
form_class = _security.login_form
@@ -182,18 +181,17 @@
login_user(form.user, remember=remember_me, authn_via=["password"])
after_this_request(_commit)
- if not _security._want_json(request):
- return redirect(get_post_login_redirect())
+ if _security._want_json(request):
+ return base_render_json(form, include_auth_token=True)
+ return redirect(get_post_login_redirect())
if _security._want_json(request):
if current_user.is_authenticated:
form.user = current_user
- return base_render_json(form, include_auth_token=True)
+ return base_render_json(form)
if current_user.is_authenticated:
- # Basically a no-op if authenticated - just perform the same
- # post-login redirect as if user just logged in.
- return redirect(get_post_login_redirect())
+ return redirect(get_url(_security.post_login_view))
else:
return _security.render_template(
config_value("LOGIN_USER_TEMPLATE"), login_user_form=form,
**_ctx("login")
@@ -625,16 +623,18 @@
if form.validate_on_submit():
after_this_request(_commit)
change_user_password(current_user._get_current_object(),
form.new_password.data)
- if not _security._want_json(request):
- do_flash(*get_message("PASSWORD_CHANGE"))
- return redirect(
- get_url(_security.post_change_view)
- or get_url(_security.post_login_view)
- )
+ if _security._want_json(request):
+ form.user = current_user
+ return base_render_json(form, include_auth_token=True)
+
+ do_flash(*get_message("PASSWORD_CHANGE"))
+ return redirect(
+ get_url(_security.post_change_view) or
get_url(_security.post_login_view)
+ )
if _security._want_json(request):
form.user = current_user
- return base_render_json(form, include_auth_token=True)
+ return base_render_json(form)
return _security.render_template(
config_value("CHANGE_PASSWORD_TEMPLATE"),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/pytest.ini
new/Flask-Security-Too-3.4.5/pytest.ini
--- old/Flask-Security-Too-3.4.3/pytest.ini 2020-06-13 18:53:19.000000000
+0200
+++ new/Flask-Security-Too-3.4.5/pytest.ini 2021-01-08 21:02:54.000000000
+0100
@@ -1,5 +1,5 @@
[pytest]
-addopts = -rs --cov flask_security --cov-report term-missing --black --flake8
--cache-clear
+addopts = -rs --cov flask_security --cov-report term-missing --flake8
--cache-clear
flake8-max-line-length = 88
flake8-ignore =
tests/view_scaffold.py E402
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/setup.py
new/Flask-Security-Too-3.4.5/setup.py
--- old/Flask-Security-Too-3.4.3/setup.py 2020-06-13 18:53:19.000000000
+0200
+++ new/Flask-Security-Too-3.4.5/setup.py 2021-01-08 21:02:54.000000000
+0100
@@ -13,7 +13,7 @@
version = re.search(r'__version__ = "(.*?)"', f.read()).group(1)
tests_require = [
- "Flask-Mongoengine>=0.9.5",
+ "Flask-Mongoengine~=0.9.5",
"peewee>=3.11.2",
"Flask-SQLAlchemy>=2.3",
"argon2_cffi>=19.1.0",
@@ -24,8 +24,8 @@
"cryptography>=2.3.1",
"isort>=4.2.2",
"mock>=1.3.0",
- "mongoengine>=0.15.3",
- "mongomock>=3.14.0",
+ "mongoengine~=0.19.1",
+ "mongomock~=3.19.0",
"msgcheck>=2.9",
"pony>=0.7.11",
"phonenumberslite>=8.11.1",
@@ -33,6 +33,7 @@
"pydocstyle>=1.0.0",
"pymysql>=0.9.3",
"pyqrcode>=1.2",
+ "pytest==4.6.11",
"pytest-black>=0.3.8",
"pytest-cache>=1.0",
"pytest-cov>=2.5.1",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/tests/conftest.py
new/Flask-Security-Too-3.4.5/tests/conftest.py
--- old/Flask-Security-Too-3.4.3/tests/conftest.py 2020-06-13
18:53:19.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/tests/conftest.py 2021-01-08
21:02:54.000000000 +0100
@@ -235,6 +235,7 @@
class Role(db.Document, RoleMixin):
name = db.StringField(required=True, unique=True, max_length=80)
description = db.StringField(max_length=255)
+ permissions = db.StringField(max_length=255)
meta = {"db_alias": db_name}
class User(db.Document, UserMixin):
@@ -347,6 +348,7 @@
id = Column(Integer(), primary_key=True)
name = Column(String(80), unique=True)
description = Column(String(255))
+ permissions = Column(String(255))
class User(Base, UserMixin):
__tablename__ = "user"
@@ -424,12 +426,14 @@
db = FlaskDB(app)
- class Role(db.Model, RoleMixin):
+ class Role(RoleMixin, db.Model):
name = CharField(unique=True, max_length=80)
description = TextField(null=True)
+ permissions = TextField(null=True)
- class User(db.Model, UserMixin):
+ class User(UserMixin, db.Model):
email = TextField()
+ fs_uniquifier = TextField(unique=True, null=False)
username = TextField()
security_number = IntegerField(null=True)
password = TextField(null=True)
@@ -455,6 +459,9 @@
name = property(lambda self: self.role.name)
description = property(lambda self: self.role.description)
+ def get_permissions(self):
+ return self.role.get_permissions()
+
with app.app_context():
for Model in (Role, User, UserRoles):
Model.drop_table()
@@ -600,6 +607,27 @@
return app.test_client(use_cookies=False)
[email protected](params=["cl-sqlalchemy", "c2", "cl-mongo", "cl-peewee"])
+def clients(request, app, tmpdir, realdburl):
+ if request.param == "cl-sqlalchemy":
+ ds = sqlalchemy_setup(request, app, tmpdir, realdburl)
+ elif request.param == "c2":
+ ds = sqlalchemy_session_setup(request, app, tmpdir, realdburl)
+ elif request.param == "cl-mongo":
+ ds = mongoengine_setup(request, app, tmpdir, realdburl)
+ elif request.param == "cl-peewee":
+ ds = peewee_setup(request, app, tmpdir, realdburl)
+ elif request.param == "cl-pony":
+ # Not working yet.
+ ds = pony_setup(request, app, tmpdir, realdburl)
+ app.security = Security(app, datastore=ds)
+ populate_data(app)
+ if request.param == "cl-peewee":
+ # peewee is insistent on a single connection?
+ ds.db.close_db(None)
+ return app.test_client()
+
+
@pytest.yield_fixture()
def in_app_context(request, sqlalchemy_app):
app = sqlalchemy_app()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/tests/test_common.py
new/Flask-Security-Too-3.4.5/tests/test_common.py
--- old/Flask-Security-Too-3.4.3/tests/test_common.py 2020-06-13
18:53:19.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/tests/test_common.py 2021-01-08
21:02:54.000000000 +0100
@@ -98,9 +98,9 @@
def test_get_already_authenticated_next(client):
response = authenticate(client, follow_redirects=True)
assert b"Welcome [email protected]" in response.data
- # This should override post_login_view
+ # This should NOT override post_login_view due to potential redirect loops.
response = client.get("/login?next=/page1", follow_redirects=True)
- assert b"Page 1" in response.data
+ assert b"Post Login" in response.data
@pytest.mark.settings(post_login_view="/post_login")
@@ -110,8 +110,9 @@
data = dict(email="[email protected]", password="password")
response = client.post("/login", data=data, follow_redirects=True)
assert b"Post Login" in response.data
+ # This should NOT override post_login_view due to potential redirect loops.
response = client.post("/login?next=/page1", data=data,
follow_redirects=True)
- assert b"Page 1" in response.data
+ assert b"Post Login" in response.data
def test_login_form(client):
@@ -268,42 +269,42 @@
@pytest.mark.settings(unauthorized_view="/unauthz")
-def test_roles_accepted(client):
+def test_roles_accepted(clients):
# This specificaly tests that we can pass a URL for unauthorized_view.
for user in ("[email protected]", "[email protected]"):
- authenticate(client, user)
- response = client.get("/admin_or_editor")
+ authenticate(clients, user)
+ response = clients.get("/admin_or_editor")
assert b"Admin or Editor Page" in response.data
- logout(client)
+ logout(clients)
- authenticate(client, "[email protected]")
- response = client.get("/admin_or_editor", follow_redirects=True)
+ authenticate(clients, "[email protected]")
+ response = clients.get("/admin_or_editor", follow_redirects=True)
assert b"Unauthorized" in response.data
@pytest.mark.settings(unauthorized_view="unauthz")
-def test_permissions_accepted(client):
+def test_permissions_accepted(clients):
for user in ("[email protected]", "[email protected]"):
- authenticate(client, user)
- response = client.get("/admin_perm")
+ authenticate(clients, user)
+ response = clients.get("/admin_perm")
assert b"Admin Page with full-write or super" in response.data
- logout(client)
+ logout(clients)
- authenticate(client, "[email protected]")
- response = client.get("/admin_perm", follow_redirects=True)
+ authenticate(clients, "[email protected]")
+ response = clients.get("/admin_perm", follow_redirects=True)
assert b"Unauthorized" in response.data
@pytest.mark.settings(unauthorized_view="unauthz")
-def test_permissions_required(client):
+def test_permissions_required(clients):
for user in ["[email protected]"]:
- authenticate(client, user)
- response = client.get("/admin_perm_required")
+ authenticate(clients, user)
+ response = clients.get("/admin_perm_required")
assert b"Admin Page required" in response.data
- logout(client)
+ logout(clients)
- authenticate(client, "[email protected]")
- response = client.get("/admin_perm_required", follow_redirects=True)
+ authenticate(clients, "[email protected]")
+ response = clients.get("/admin_perm_required", follow_redirects=True)
assert b"Unauthorized" in response.data
@@ -314,15 +315,15 @@
@pytest.mark.settings(unauthorized_view="unauthz")
-def test_multiple_role_required(client):
+def test_multiple_role_required(clients):
for user in ("[email protected]", "[email protected]"):
- authenticate(client, user)
- response = client.get("/admin_and_editor", follow_redirects=True)
+ authenticate(clients, user)
+ response = clients.get("/admin_and_editor", follow_redirects=True)
assert b"Unauthorized" in response.data
- client.get("/logout")
+ clients.get("/logout")
- authenticate(client, "[email protected]")
- response = client.get("/admin_and_editor", follow_redirects=True)
+ authenticate(clients, "[email protected]")
+ response = clients.get("/admin_and_editor", follow_redirects=True)
assert b"Admin and Editor Page" in response.data
@@ -365,6 +366,15 @@
def test_http_auth(client):
+ # browsers expect 401 response with WWW-Authenticate header - which will
prompt
+ # them to pop up a login form.
+ response = client.get("/http", headers={})
+ assert response.status_code == 401
+ assert b"You are not authenticated" in response.data
+ assert "WWW-Authenticate" in response.headers
+ assert 'Basic realm="Login Required"' ==
response.headers["WWW-Authenticate"]
+
+ # Now provide correct credentials
response = client.get(
"/http",
headers={
@@ -505,8 +515,10 @@
assert b"Basic" in response.data
response = client.get("/multi_auth")
- # Default unauthn is to redirect
- assert response.status_code == 302
+ # Default unauthn with basic is to return 401 with WWW-Authenticate Header
+ # so that browser pops up a username/password dialog
+ assert response.status_code == 401
+ assert "WWW-Authenticate" in response.headers
@pytest.mark.settings(backwards_compat_unauthn=True)
@@ -523,7 +535,6 @@
assert 'Basic realm="Login Required"' ==
response.headers["WWW-Authenticate"]
response = client.get("/multi_auth")
- print(response.headers)
assert response.status_code == 401
@@ -540,6 +551,20 @@
assert b"Session" in response.data
+def test_authenticated_loop(client):
+ # If user is already authenticated say via session, and then hits an
endpoint
+ # protected with @auth_token_required() - then they will be redirected to
the login
+ # page which will simply note the current user is already logged in and
redirect
+ # to POST_LOGIN_VIEW. Between 3.3.0 and 3.4.4 - this redirect would honor
the 'next'
+ # parameter - thus redirecting back to the endpoint that caused the
redirect in the
+ # first place - thus an infinite loop.
+ authenticate(client)
+
+ response = client.get("/token", follow_redirects=True)
+ assert response.status_code == 200
+ assert b"Home Page" in response.data
+
+
def test_user_deleted_during_session_reverts_to_anonymous_user(app, client):
authenticate(client)
@@ -724,3 +749,26 @@
assert response.status_code == 200
end_nqueries = get_num_queries(app.security.datastore)
assert current_nqueries is None or end_nqueries == (current_nqueries + 2)
+
+
[email protected]()
+def test_no_get_auth_token(app, client):
+ # Test that GETs don't return an auth token. This is a security issue since
+ # GETs aren't protected with CSRF
+ authenticate(client)
+ response = client.get(
+ "/login?include_auth_token", headers={"Content-Type":
"application/json"}
+ )
+ assert "authentication_token" not in response.json["response"]["user"]
+
+ data = dict(
+ password="password",
+ new_password="new strong password",
+ new_password_confirm="new strong password",
+ )
+ response = client.get(
+ "/change?include_auth_token",
+ json=data,
+ headers={"Content-Type": "application/json"},
+ )
+ assert "authentication_token" not in response.json["response"]["user"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/tests/test_configuration.py
new/Flask-Security-Too-3.4.5/tests/test_configuration.py
--- old/Flask-Security-Too-3.4.3/tests/test_configuration.py 2020-06-13
18:53:19.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/tests/test_configuration.py 2021-01-08
21:02:54.000000000 +0100
@@ -35,8 +35,10 @@
"/http",
headers={"Authorization": "Basic %s" %
base64.b64encode(b"[email protected]:bogus")},
)
- assert response.status_code == 302
- assert response.headers["Location"] ==
"http://localhost/custom_login?next=%2Fhttp"
+ assert response.status_code == 401
+ assert b"You are not authenticated" in response.data
+ assert "WWW-Authenticate" in response.headers
+ assert 'Basic realm="Custom Realm"' == response.headers["WWW-Authenticate"]
@pytest.mark.settings(login_user_template="custom_security/login_user.html")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/tests/test_datastore.py
new/Flask-Security-Too-3.4.5/tests/test_datastore.py
--- old/Flask-Security-Too-3.4.3/tests/test_datastore.py 2020-06-13
18:53:19.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/tests/test_datastore.py 2021-01-08
21:02:54.000000000 +0100
@@ -258,6 +258,7 @@
user = datastore.find_user(email="[email protected]")
assert user.has_role("test1") is True
assert user.has_permission("read") is True
+ assert user.has_permission("write") is False
def test_permissions_strings(app, datastore):
@@ -305,20 +306,22 @@
t1 = ds.find_role("test1")
assert perms == t1.get_permissions()
- orig_update_time = t1.update_datetime
- assert t1.update_datetime <= datetime.datetime.utcnow()
+ if hasattr(t1, "update_datetime"):
+ orig_update_time = t1.update_datetime
+ assert t1.update_datetime <= datetime.datetime.utcnow()
- t1.add_permissions("execute")
+ ds.add_permissions_to_role(t1, "execute")
ds.commit()
t1 = ds.find_role("test1")
assert perms.union({"execute"}) == t1.get_permissions()
- t1.remove_permissions("read")
+ ds.remove_permissions_from_role(t1, "read")
ds.commit()
t1 = ds.find_role("test1")
assert {"write", "execute"} == t1.get_permissions()
- assert t1.update_datetime > orig_update_time
+ if hasattr(t1, "update_datetime"):
+ assert t1.update_datetime > orig_update_time
def test_get_permissions(app, datastore):
@@ -350,13 +353,13 @@
assert {"read", "write"} == t1.get_permissions()
# send in a list
- t1.add_permissions(["execute", "whatever"])
+ ds.add_permissions_to_role(t1, ["execute", "whatever"])
ds.commit()
t1 = ds.find_role("test1")
assert {"read", "write", "execute", "whatever"} == t1.get_permissions()
- t1.remove_permissions(["read", "whatever"])
+ ds.remove_permissions_from_role(t1, ["read", "whatever"])
ds.commit()
assert {"write", "execute"} == t1.get_permissions()
@@ -366,13 +369,13 @@
ds.commit()
t2 = ds.find_role("test2")
- t2.add_permissions({"execute", "whatever"})
+ ds.add_permissions_to_role(t2, {"execute", "whatever"})
ds.commit()
t2 = ds.find_role("test2")
assert {"read", "write", "execute", "whatever"} == t2.get_permissions()
- t2.remove_permissions({"read", "whatever"})
+ ds.remove_permissions_from_role(t2, {"read", "whatever"})
ds.commit()
assert {"write", "execute"} == t2.get_permissions()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/tests/test_response.py
new/Flask-Security-Too-3.4.5/tests/test_response.py
--- old/Flask-Security-Too-3.4.3/tests/test_response.py 2020-06-13
18:53:19.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/tests/test_response.py 2021-01-08
21:02:54.000000000 +0100
@@ -51,11 +51,11 @@
def test_default_unauthn(app, client):
""" Test default unauthn handler with and without json """
- response = client.get("/multi_auth")
+ response = client.get("/profile")
assert response.status_code == 302
- assert response.headers["Location"] ==
"http://localhost/login?next=%2Fmulti_auth"
+ assert response.headers["Location"] ==
"http://localhost/login?next=%2Fprofile"
- response = client.get("/multi_auth", headers={"Accept":
"application/json"})
+ response = client.get("/profile", headers={"Accept": "application/json"})
assert response.status_code == 401
assert response.json["meta"]["code"] == 401
# While "Basic" is acceptable, we never get a WWW-Authenticate header back
since
@@ -67,11 +67,11 @@
def test_default_unauthn_bp(app, client):
""" Test default unauthn handler with blueprint prefix and login url """
- response = client.get("/multi_auth")
+ response = client.get("/profile")
assert response.status_code == 302
assert (
response.headers["Location"]
- == "http://localhost/myprefix/mylogin?next=%2Fmulti_auth"
+ == "http://localhost/myprefix/mylogin?next=%2Fprofile"
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Flask-Security-Too-3.4.3/tests/view_scaffold.py
new/Flask-Security-Too-3.4.5/tests/view_scaffold.py
--- old/Flask-Security-Too-3.4.3/tests/view_scaffold.py 2020-06-13
18:53:19.000000000 +0200
+++ new/Flask-Security-Too-3.4.5/tests/view_scaffold.py 2021-01-08
21:02:54.000000000 +0100
@@ -29,6 +29,7 @@
from flask.json import JSONEncoder
from flask_security import (
Security,
+ auth_required,
current_user,
login_required,
SQLAlchemySessionUserDatastore,
@@ -170,6 +171,11 @@
email=current_user.email,
)
+ @app.route("/basicauth")
+ @auth_required("basic")
+ def basic():
+ return render_template_string("Basic auth success")
+
return app
++++++ fix-dependencies.patch ++++++
--- /var/tmp/diff_new_pack.YlLHAO/_old 2021-07-08 22:49:36.031921997 +0200
+++ /var/tmp/diff_new_pack.YlLHAO/_new 2021-07-08 22:49:36.031921997 +0200
@@ -5,7 +5,7 @@
@@ -14,20 +14,19 @@ with io.open("flask_security/__init__.py
tests_require = [
- "Flask-Mongoengine>=0.9.5",
+ "Flask-Mongoengine~=0.9.5",
- "peewee>=3.11.2",
+ "peewee>=3.7.1",
"Flask-SQLAlchemy>=2.3",
@@ -19,8 +19,8 @@
+ "cryptography>=2.1.4",
"isort>=4.2.2",
"mock>=1.3.0",
- "mongoengine>=0.15.3",
- "mongomock>=3.14.0",
+ "mongoengine~=0.19.1",
+ "mongomock~=3.19.0",
"msgcheck>=2.9",
- "pony>=0.7.11",
"phonenumberslite>=8.11.1",
@@ -42,7 +42,7 @@
@@ -13,20 +13,19 @@ Pallets-Sphinx-Themes>=1.2.0
Sphinx>=1.8.5
sphinx-issues>=1.2.0
- Flask-Mongoengine>=0.9.5
+ Flask-Mongoengine~=0.9.5
-peewee>=3.11.2
+peewee>=3.7.1
Flask-SQLAlchemy>=2.3
@@ -56,8 +56,8 @@
+cryptography>=2.1.4
isort>=4.2.2
mock>=1.3.0
- mongoengine>=0.15.3
- mongomock>=3.14.0
+ mongoengine~=0.19.1
+ mongomock~=3.19.0
msgcheck>=2.9
-pony>=0.7.11
phonenumberslite>=8.11.1
@@ -73,7 +73,7 @@
Pallets-Sphinx-Themes>=1.2.0
Sphinx>=1.8.5
sphinx-issues>=1.2.0
- Flask-Mongoengine>=0.9.5
+ Flask-Mongoengine~=0.9.5
-peewee>=3.11.2
+peewee>=3.7.1
Flask-SQLAlchemy>=2.3
@@ -87,8 +87,8 @@
+cryptography>=2.1.4
isort>=4.2.2
mock>=1.3.0
- mongoengine>=0.15.3
- mongomock>=3.14.0
+ mongoengine~=0.19.1
+ mongomock~=3.19.0
msgcheck>=2.9
-pony>=0.7.11
phonenumberslite>=8.11.1
@@ -106,7 +106,7 @@
@@ -83,20 +81,19 @@ sphinx-issues>=1.2.0
[tests]
- Flask-Mongoengine>=0.9.5
+ Flask-Mongoengine~=0.9.5
-peewee>=3.11.2
+peewee>=3.7.1
Flask-SQLAlchemy>=2.3
@@ -120,8 +120,8 @@
+cryptography>=2.1.4
isort>=4.2.2
mock>=1.3.0
- mongoengine>=0.15.3
- mongomock>=3.14.0
+ mongoengine~=0.19.1
+ mongomock~=3.19.0
msgcheck>=2.9
-pony>=0.7.11
phonenumberslite>=8.11.1
++++++ no-mongodb.patch ++++++
--- /var/tmp/diff_new_pack.YlLHAO/_old 2021-07-08 22:49:36.039921936 +0200
+++ /var/tmp/diff_new_pack.YlLHAO/_new 2021-07-08 22:49:36.043921905 +0200
@@ -1,8 +1,17 @@
-Index: Flask-Security-Too-3.4.0/tests/conftest.py
+Index: Flask-Security-Too-3.4.5/tests/conftest.py
===================================================================
---- Flask-Security-Too-3.4.0.orig/tests/conftest.py
-+++ Flask-Security-Too-3.4.0/tests/conftest.py
-@@ -617,7 +617,7 @@ def get_message(app):
+--- Flask-Security-Too-3.4.5.orig/tests/conftest.py
++++ Flask-Security-Too-3.4.5/tests/conftest.py
+@@ -607,7 +607,7 @@ def client_nc(request, sqlalchemy_app):
+ return app.test_client(use_cookies=False)
+
+
[email protected](params=["cl-sqlalchemy", "c2", "cl-mongo", "cl-peewee"])
[email protected](params=["cl-sqlalchemy", "c2", "cl-peewee"])
+ def clients(request, app, tmpdir, realdburl):
+ if request.param == "cl-sqlalchemy":
+ ds = sqlalchemy_setup(request, app, tmpdir, realdburl)
+@@ -645,7 +645,7 @@ def get_message(app):
@pytest.fixture(