Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User account page shows non-functioning password reset option #195

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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