Merge ~adam-collard/maas:websocket-user-change-password into maas:master

Proposed by Adam Collard
Status: Merged
Approved by: Adam Collard
Approved revision: 9c40e496b7f1aef1036b870c950010af6349f4ab
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~adam-collard/maas:websocket-user-change-password
Merge into: maas:master
Diff against target: 111 lines (+80/-1)
2 files modified
src/maasserver/websockets/handlers/tests/test_user.py (+64/-0)
src/maasserver/websockets/handlers/user.py (+16/-1)
Reviewer Review Type Date Requested Status
MAAS Lander Approve
Alberto Donato (community) Approve
Review via email: mp+392616@code.launchpad.net

Commit message

LP 1894727: Add websocket endpoint for superusers to change password

To post a comment you must log in.
Revision history for this message
Adam Collard (adam-collard) wrote :

Open question as to whether this is suitable for use by UI.

The UX right now seems to have one form for user details and passwords. This requires logic in the JS app to direct user details to one endpoint and password changes to another, multiplexing the responses and error messages (which I appreciate is non-trivial).

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b websocket-user-change-password lp:~adam-collard/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: f76a8607261f8c1a7e004c92e15aad6d259fbe73

review: Approve
Revision history for this message
Alberto Donato (ack) wrote :

+1 with a comment inline

Revision history for this message
Alberto Donato (ack) :
review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b websocket-user-change-password lp:~adam-collard/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 9c40e496b7f1aef1036b870c950010af6349f4ab

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

LANDING
-b websocket-user-change-password lp:~adam-collard/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED BUILD
LOG: http://maas-ci.internal:8080/job/maas/job/branch-tester/8553/consoleText

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/websockets/handlers/tests/test_user.py b/src/maasserver/websockets/handlers/tests/test_user.py
2index 67bcd6e..d221fa2 100644
3--- a/src/maasserver/websockets/handlers/tests/test_user.py
4+++ b/src/maasserver/websockets/handlers/tests/test_user.py
5@@ -394,3 +394,67 @@ class TestUserHandler(MAASServerTestCase):
6 )
7 self.assertEqual(self.dehydrate_user(user, for_self=True), observed)
8 self.assertTrue(user.check_password("newpassword"))
9+
10+ def test_change_other_users_password_as_admin(self):
11+ admin_user = factory.make_admin()
12+ handler = UserHandler(admin_user, {}, None)
13+ user = factory.make_User()
14+
15+ response = handler.admin_change_password(
16+ {
17+ "id": user.id,
18+ "password1": "newpassword",
19+ "password2": "newpassword",
20+ }
21+ )
22+ user = reload_object(user)
23+
24+ self.assertIsNone(response)
25+ self.assertTrue(
26+ user.check_password("newpassword"),
27+ "Password not correctly changed",
28+ )
29+
30+ def test_cannot_change_other_users_password_as_unprivileged(self):
31+ unprivileged_user = factory.make_User()
32+ handler = UserHandler(unprivileged_user, {}, None)
33+ user = factory.make_User()
34+
35+ self.assertRaises(
36+ HandlerPermissionError,
37+ handler.admin_change_password,
38+ {
39+ "id": user.id,
40+ "password1": "newpassword",
41+ "password2": "newpassword",
42+ },
43+ )
44+
45+ def test_cannot_change_own_password_as_unprivileged_using_admin(self):
46+ unprivileged_user = factory.make_User()
47+ handler = UserHandler(unprivileged_user, {}, None)
48+
49+ self.assertRaises(
50+ HandlerPermissionError,
51+ handler.admin_change_password,
52+ {
53+ "id": unprivileged_user.id,
54+ "password1": "newpassword",
55+ "password2": "newpassword",
56+ },
57+ )
58+
59+ def test_cannot_change_other_users_password_as_admin_bad_password(self):
60+ admin_user = factory.make_admin()
61+ handler = UserHandler(admin_user, {}, None)
62+ user = factory.make_User()
63+
64+ self.assertRaises(
65+ HandlerValidationError,
66+ handler.admin_change_password,
67+ {
68+ "id": user.id,
69+ "password1": "foo",
70+ "password2": "bar",
71+ },
72+ )
73diff --git a/src/maasserver/websockets/handlers/user.py b/src/maasserver/websockets/handlers/user.py
74index 7a12ac0..865f331 100644
75--- a/src/maasserver/websockets/handlers/user.py
76+++ b/src/maasserver/websockets/handlers/user.py
77@@ -5,7 +5,10 @@
78
79 __all__ = ["UserHandler"]
80
81-from django.contrib.auth.forms import PasswordChangeForm
82+from django.contrib.auth.forms import (
83+ AdminPasswordChangeForm,
84+ PasswordChangeForm,
85+)
86 from django.contrib.auth.models import User
87 from django.db.models import Count
88 from django.http import HttpRequest
89@@ -51,6 +54,7 @@ class UserHandler(Handler):
90 "auth_user",
91 "mark_intro_complete",
92 "change_password",
93+ "admin_change_password",
94 ]
95 fields = [
96 "id",
97@@ -208,3 +212,14 @@ class UserHandler(Handler):
98 return self.full_dehydrate(self.user)
99 else:
100 raise HandlerValidationError(form.errors)
101+
102+ def admin_change_password(self, params):
103+ """As Admin, update another user's password."""
104+ if not self.user.is_superuser:
105+ raise HandlerPermissionError()
106+ user = self.get_object(params)
107+ form = AdminPasswordChangeForm(user=user, data=get_QueryDict(params))
108+ if form.is_valid():
109+ form.save()
110+ else:
111+ raise HandlerValidationError(form.errors)

Subscribers

People subscribed via source and target branches