From 52fb367c44a2bb8348d6167851e25fb5a75d04d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alban=20F=C3=A9ron?= Date: Tue, 10 Nov 2020 16:56:49 +0100 Subject: [PATCH] Implement updateUser --- docs/api.md | 14 +++++------ supysonic/api/user.py | 29 ++++++++++++++++++++++ supysonic/managers/user.py | 15 ++++++++---- tests/api/test_user.py | 37 ++++++++++++++++++++++++++++- tests/managers/test_manager_user.py | 10 ++++++-- 5 files changed, 90 insertions(+), 15 deletions(-) diff --git a/docs/api.md b/docs/api.md index 95603e8..810e481 100644 --- a/docs/api.md +++ b/docs/api.md @@ -95,7 +95,7 @@ or with version 1.8.0. | [`getUser`](#getuser) | | ✔️ | | [`getUsers`](#getusers) | 1.9.0 | ✔️ | | [`createUser`](#createuser) | | ✔️ | -| [`updateUser`](#updateuser) | 1.10.2 | 📅 | +| [`updateUser`](#updateuser) | 1.10.2 | ✔️ | | [`deleteUser`](#deleteuser) | | ✔️ | | [`changePassword`](#changepassword) | | ✔️ | | [`getBookmarks`](#getbookmarks) | 1.9.0 | ❔ | @@ -703,18 +703,18 @@ No parameter | `musicFolderId` | 1.12.0 | 📅 | #### `updateUser` -📅 1.10.2 +✔️ 1.10.2 | Parameter | Vers. | | |-----------------------|--------|---| -| `username` | 1.10.2 | 📅 | -| `password` | 1.10.2 | 📅 | -| `email` | 1.10.2 | 📅 | +| `username` | 1.10.2 | ✔️ | +| `password` | 1.10.2 | ✔️ | +| `email` | 1.10.2 | ✔️ | | `ldapAuthenticated` | 1.10.2 | | -| `adminRole` | 1.10.2 | 📅 | +| `adminRole` | 1.10.2 | ✔️ | | `settingsRole` | 1.10.2 | | | `streamRole` | 1.10.2 | | -| `jukeboxRole` | 1.10.2 | 📅 | +| `jukeboxRole` | 1.10.2 | ✔️ | | `downloadRole` | 1.10.2 | | | `uploadRole` | 1.10.2 | | | `coverArtRole` | 1.10.2 | | diff --git a/supysonic/api/user.py b/supysonic/api/user.py index fc84a55..9c744a9 100644 --- a/supysonic/api/user.py +++ b/supysonic/api/user.py @@ -92,3 +92,32 @@ def user_changepass(): UserManager.change_password2(username, password) return request.formatter.empty + + +@api.route("/updateUser.view", methods=["GET", "POST"]) +@admin_only +def user_edit(): + username = request.values["username"] + user = User.get(name=username) + if user is None: + raise NotFound("User") + + if "password" in request.values: + password = decode_password(request.values["password"]) + UserManager.change_password2(user, password) + + email, admin, jukebox = map( + request.values.get, ["email", "adminRole", "jukeboxRole"] + ) + if email is not None: + user.mail = email + + if admin is not None: + admin = admin in (True, "True", "true", 1, "1") + user.admin = admin + + if jukebox is not None: + jukebox = jukebox in (True, "True", "true", 1, "1") + user.jukebox = jukebox + + return request.formatter.empty diff --git a/supysonic/managers/user.py b/supysonic/managers/user.py index c1978b4..dceb237 100644 --- a/supysonic/managers/user.py +++ b/supysonic/managers/user.py @@ -24,7 +24,7 @@ class UserManager: elif isinstance(uid, str): uid = uuid.UUID(uid) else: - raise ValueError("Invalid user id") + raise TypeError("Invalid user id") return User[uid] @@ -67,10 +67,15 @@ class UserManager: user.password = UserManager.__encrypt_password(new_pass, user.salt)[0] @staticmethod - def change_password2(name, new_pass): - user = User.get(name=name) - if user is None: - raise ObjectNotFound(User) + def change_password2(name_or_user, new_pass): + if isinstance(name_or_user, User): + user = name_or_user + elif isinstance(name_or_user, str): + user = User.get(name=name_or_user) + if user is None: + raise ObjectNotFound(User) + else: + raise TypeError("Requires a User instance or a user name (string)") user.password = UserManager.__encrypt_password(new_pass, user.salt)[0] diff --git a/tests/api/test_user.py b/tests/api/test_user.py index d23b968..b3043ca 100644 --- a/tests/api/test_user.py +++ b/tests/api/test_user.py @@ -222,7 +222,7 @@ class UserTestCase(ApiTestBase): # non ASCII in hex encoded password self._make_request( "changePassword", - {"username": "alice", "password": "enc:" + hexlify(u"новыйпароль")}, + {"username": "alice", "password": "enc:" + hexlify("новыйпароль")}, skip_post=True, ) self._make_request("ping", {"u": "alice", "p": "новыйпароль"}) @@ -240,6 +240,41 @@ class UserTestCase(ApiTestBase): ) self._make_request("ping", {"u": "alice", "p": "enc:randomstring"}) + def test_update_user(self): + # non admin + self._make_request( + "updateUser", {"u": "bob", "p": "B0b", "username": "alice"}, error=50 + ) + + # missing param + self._make_request("updateUser", error=10) + + # non existing + self._make_request("updateUser", {"username": "charlie"}, error=70) + + self._make_request( + "updateUser", + {"username": "bob", "email": "email@email.em", "jukeboxRole": True}, + ) + rv, child = self._make_request("getUser", {"username": "bob"}, tag="user") + self.assertEqual(child.get("email"), "email@email.em") + self.assertEqual(child.get("adminRole"), "false") + self.assertEqual(child.get("jukeboxRole"), "true") + + self._make_request( + "updateUser", {"username": "bob", "email": "example@email.com"} + ) + rv, child = self._make_request("getUser", {"username": "bob"}, tag="user") + self.assertEqual(child.get("email"), "example@email.com") + self.assertEqual(child.get("adminRole"), "false") + self.assertEqual(child.get("jukeboxRole"), "true") + + self._make_request("updateUser", {"username": "bob", "adminRole": True}) + rv, child = self._make_request("getUser", {"username": "bob"}, tag="user") + self.assertEqual(child.get("email"), "example@email.com") + self.assertEqual(child.get("adminRole"), "true") + self.assertEqual(child.get("jukeboxRole"), "true") + if __name__ == "__main__": unittest.main() diff --git a/tests/managers/test_manager_user.py b/tests/managers/test_manager_user.py index ecb1708..f9a3b63 100644 --- a/tests/managers/test_manager_user.py +++ b/tests/managers/test_manager_user.py @@ -85,7 +85,7 @@ class UserManagerTestCase(unittest.TestCase): # Get with invalid UUID self.assertRaises(ValueError, UserManager.get, "invalid-uuid") - self.assertRaises(ValueError, UserManager.get, 0xFEE1BAD) + self.assertRaises(TypeError, UserManager.get, 0xFEE1BAD) # Non-existent user self.assertRaises(ObjectNotFound, UserManager.get, uuid.uuid4()) @@ -104,7 +104,7 @@ class UserManagerTestCase(unittest.TestCase): # Delete invalid UUID self.assertRaises(ValueError, UserManager.delete, "invalid-uuid") - self.assertRaises(ValueError, UserManager.delete, 0xFEE1B4D) + self.assertRaises(TypeError, UserManager.delete, 0xFEE1B4D) self.assertEqual(db.User.select().count(), 3) # Delete non-existent user @@ -190,6 +190,8 @@ class UserManagerTestCase(unittest.TestCase): def test_change_password2(self): self.create_data() + self.assertRaises(TypeError, UserManager.change_password2, uuid.uuid4(), "pass") + # With existing users for name in ["alice", "bob", "charlie"]: UserManager.change_password2(name, "newpass") @@ -197,6 +199,10 @@ class UserManagerTestCase(unittest.TestCase): self.assertEqual(UserManager.try_auth(name, "newpass"), user) self.assertEqual(UserManager.try_auth(name, name.upper()), None) + # test passing the user directly + UserManager.change_password2(user, "NEWPASS") + self.assertEqual(UserManager.try_auth(name, "NEWPASS"), user) + # Non-existent user self.assertRaises( ObjectNotFound, UserManager.change_password2, "null", "newpass"