Merge ~blake-rouse/maas:fix-1844793 into maas:master

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: e128f94d025350034e340eae0c48c92680dda2fb
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~blake-rouse/maas:fix-1844793
Merge into: maas:master
Diff against target: 87 lines (+38/-0)
2 files modified
src/maasserver/websockets/handlers/tests/test_user.py (+23/-0)
src/maasserver/websockets/handlers/user.py (+15/-0)
Reviewer Review Type Date Requested Status
Alberto Donato (community) Approve
Review via email: mp+373134@code.launchpad.net

Commit message

Fixes LP: #1844793 - Add ability for a user to change password over the websocket API.

To post a comment you must log in.
Revision history for this message
Alberto Donato (ack) :
Revision history for this message
Blake Rouse (blake-rouse) :
Revision history for this message
Alberto Donato (ack) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/src/maasserver/websockets/handlers/tests/test_user.py b/src/maasserver/websockets/handlers/tests/test_user.py
index ae0e8ee..2c69685 100644
--- a/src/maasserver/websockets/handlers/tests/test_user.py
+++ b/src/maasserver/websockets/handlers/tests/test_user.py
@@ -30,6 +30,7 @@ from maasserver.websockets.base import (
30 dehydrate_datetime,30 dehydrate_datetime,
31 HandlerDoesNotExistError,31 HandlerDoesNotExistError,
32 HandlerPermissionError,32 HandlerPermissionError,
33 HandlerValidationError,
33)34)
34from maasserver.websockets.handlers.user import UserHandler35from maasserver.websockets.handlers.user import UserHandler
35from maastesting.djangotestcase import count_queries36from maastesting.djangotestcase import count_queries
@@ -348,3 +349,25 @@ class TestUserHandler(MAASServerTestCase):
348 handler = UserHandler(user, {}, None)349 handler = UserHandler(user, {}, None)
349 last_login_serialised = handler.get({"id": user.id})['last_login']350 last_login_serialised = handler.get({"id": user.id})['last_login']
350 self.assertEqual(last_login_serialised, now.strftime(DATETIME_FORMAT))351 self.assertEqual(last_login_serialised, now.strftime(DATETIME_FORMAT))
352
353 def test_change_password_invalid(self):
354 user = factory.make_User()
355 user.set_password("oldpassword")
356 handler = UserHandler(user, {}, None)
357 self.assertRaises(HandlerValidationError, handler.change_password, {
358 "new_password1": "newpassword",
359 "new_password2": "mismatchpassword",
360 "old_password": "oldpassword",
361 })
362
363 def test_change_password(self):
364 user = factory.make_User()
365 user.set_password("oldpassword")
366 handler = UserHandler(user, {}, None)
367 observed = handler.change_password({
368 "new_password1": "newpassword",
369 "new_password2": "newpassword",
370 "old_password": "oldpassword",
371 })
372 self.assertEqual(self.dehydrate_user(user, for_self=True), observed)
373 self.assertTrue(user.check_password("newpassword"))
diff --git a/src/maasserver/websockets/handlers/user.py b/src/maasserver/websockets/handlers/user.py
index d9ea2cc..c046a91 100644
--- a/src/maasserver/websockets/handlers/user.py
+++ b/src/maasserver/websockets/handlers/user.py
@@ -7,6 +7,7 @@ __all__ = [
7 "UserHandler",7 "UserHandler",
8]8]
99
10from django.contrib.auth.forms import PasswordChangeForm
10from django.contrib.auth.models import User11from django.contrib.auth.models import User
11from django.db.models import Count12from django.db.models import Count
12from django.http import HttpRequest13from django.http import HttpRequest
@@ -22,11 +23,13 @@ from maasserver.permissions import (
22 PodPermission,23 PodPermission,
23 ResourcePoolPermission,24 ResourcePoolPermission,
24)25)
26from maasserver.utils.forms import get_QueryDict
25from maasserver.websockets.base import (27from maasserver.websockets.base import (
26 dehydrate_datetime,28 dehydrate_datetime,
27 Handler,29 Handler,
28 HandlerDoesNotExistError,30 HandlerDoesNotExistError,
29 HandlerPermissionError,31 HandlerPermissionError,
32 HandlerValidationError,
30)33)
31from provisioningserver.events import EVENT_TYPES34from provisioningserver.events import EVENT_TYPES
3235
@@ -50,6 +53,7 @@ class UserHandler(Handler):
50 'create_authorisation_token',53 'create_authorisation_token',
51 'update_token_name',54 'update_token_name',
52 'delete_authorisation_token',55 'delete_authorisation_token',
56 'change_password',
53 ]57 ]
54 fields = [58 fields = [
55 "id",59 "id",
@@ -232,3 +236,14 @@ class UserHandler(Handler):
232 profile.delete_authorisation_token(params['key'])236 profile.delete_authorisation_token(params['key'])
233 self.create_audit_event(EVENT_TYPES.AUTHORISATION, "Deleted token.")237 self.create_audit_event(EVENT_TYPES.AUTHORISATION, "Deleted token.")
234 return {}238 return {}
239
240 def change_password(self, params):
241 """Update the authenticated user password."""
242 form = PasswordChangeForm(user=self.user, data=get_QueryDict(params))
243 if form.is_valid():
244 form.save()
245 self.user.sshkeys_count = self.user.sshkey_set.count()
246 self.user.machines_count = self.user.node_set.count()
247 return self.full_dehydrate(self.user)
248 else:
249 raise HandlerValidationError(form.errors)

Subscribers

People subscribed via source and target branches