Skip to content

Commit

Permalink
User account page shows non-functioning password reset option (#195)
Browse files Browse the repository at this point in the history
* Create draft PR for #188

* Allow non-admin users to change their own passwords. Let users know that access tokens are also invalidated.

* make tests pass

* changelog entry

Co-authored-by: nhoening <nhoening@users.noreply.github.com>
Co-authored-by: Nicolas Höning <nicolas@seita.nl>
  • Loading branch information
3 people committed Sep 28, 2021
1 parent cb3de7c commit bd364dc
Show file tree
Hide file tree
Showing 7 changed files with 16 additions and 18 deletions.
1 change: 1 addition & 0 deletions documentation/changelog.rst
Expand Up @@ -15,6 +15,7 @@ New features
Bugfixes
-----------
* Fix missing conversion of data source names and ids to DataSource objects [see `PR #178 <http://www.github.com/SeitaBV/flexmeasures/pull/178>`_]
* Fix users resetting their own password [see `PR #195 <http://www.github.com/SeitaBV/flexmeasures/pull/195>`_]

Infrastructure / Support
----------------------
Expand Down
5 changes: 4 additions & 1 deletion flexmeasures/api/v2_0/implementations/users.py
Expand Up @@ -123,13 +123,16 @@ def patch(db_user: UserModel, user_data: dict):
return user_schema.dump(db_user), 200


@load_user(admins_only=True)
@load_user()
@as_json
def reset_password(user):
"""
Reset the user's current password, cookies and auth tokens.
Send a password reset link to the user.
"""
# Don't allow non-admins to reset passwords of other users
if current_user.id != user.id and not current_user.has_role("admin"):
return unauthorized_handler(None, [])
set_random_password(user)
remove_cookie_and_token_access(user)
send_reset_password_instructions(user)
Expand Down
2 changes: 1 addition & 1 deletion flexmeasures/api/v2_0/routes.py
Expand Up @@ -487,7 +487,7 @@ def reset_user_password(id: int):
It sets the current password to something random, invalidates cookies and auth tokens,
and also sends an email for resetting the password to the user.
Only admins can use this endpoint.
Users can reset their own passwords. Only admins can use this endpoint to reset passwords of other users.
:reqheader Authorization: The authentication token
:reqheader Content-Type: application/json
Expand Down
13 changes: 4 additions & 9 deletions flexmeasures/api/v2_0/tests/test_api_v2_0_users_fresh_db.py
Expand Up @@ -11,14 +11,13 @@
(""),
("test_supplier@seita.nl"),
("test_prosumer@seita.nl"),
("test_prosumer@seita.nl"),
("test_prosumer@seita.nl"),
("inactive@seita.nl"),
),
)
def test_user_reset_password(app, client, sender):
def test_user_reset_password(app, client, setup_inactive_user, sender):
"""
Reset the password of supplier.
Only the prosumer is allowed to do that (as admin).
Only the prosumer (as admin) and the supplier themselves are allowed to do that.
"""
with UserContext("test_supplier@seita.nl") as supplier:
supplier_id = supplier.id
Expand All @@ -34,14 +33,10 @@ def test_user_reset_password(app, client, sender):
)
print("Server responded with:\n%s" % pwd_reset_response.json)

if sender == "":
if sender in ("", "inactive@seita.nl"):
assert pwd_reset_response.status_code == 401
return

if sender == "test_supplier@seita.nl":
assert pwd_reset_response.status_code == 403
return

assert pwd_reset_response.status_code == 200

supplier = find_user_by_email("test_supplier@seita.nl")
Expand Down
2 changes: 1 addition & 1 deletion flexmeasures/data/models/user.py
Expand Up @@ -92,7 +92,7 @@ class User(db.Model, UserMixin):
)

def __repr__(self):
return "<User %s (ID:%d)" % (self.username, self.id)
return "<User %s (ID:%d)>" % (self.username, self.id)

@property
def is_authenticated(self):
Expand Down
7 changes: 4 additions & 3 deletions flexmeasures/ui/crud/users.py
Expand Up @@ -6,7 +6,7 @@
from flask_wtf import FlaskForm
from wtforms import StringField, FloatField, DateTimeField, BooleanField
from wtforms.validators import DataRequired
from flask_security import roles_required
from flask_security import roles_required, login_required

from flexmeasures.data.config import db
from flexmeasures.data.models.user import User, Role, Account
Expand Down Expand Up @@ -126,7 +126,7 @@ def toggle_active(self, id: str):
% (patched_user.username, patched_user.active),
)

@roles_required("admin")
@login_required
def reset_password_for(self, id: str):
"""/users/reset_password_for/<id>
Set the password to something random (in case of worries the password might be compromised)
Expand All @@ -138,5 +138,6 @@ def reset_password_for(self, id: str):
return render_user(
user,
msg="The user's password has been changed to a random password"
" and password reset instructions have been sent to the user.",
" and password reset instructions have been sent to the user."
" Cookies and the API access token have also been invalidated.",
)
4 changes: 1 addition & 3 deletions flexmeasures/ui/tests/test_user_crud.py
Expand Up @@ -10,9 +10,7 @@
"""


@pytest.mark.parametrize(
"view", ["index", "get", "toggle_active", "reset_password_for"]
)
@pytest.mark.parametrize("view", ["index", "get", "toggle_active"])
def test_user_crud_as_non_admin(client, as_prosumer, view):
user_index = client.get(url_for("UserCrudUI:index"), follow_redirects=True)
assert user_index.status_code == 403
Expand Down

0 comments on commit bd364dc

Please sign in to comment.