diff --git a/API.md b/API.md index 74de9cc8..31e0b496 100644 --- a/API.md +++ b/API.md @@ -17,6 +17,7 @@ - [Creating user](#creating-user) - [Updating user](#updating-user) - [Getting user](#getting-user) + - [Removing user](#removing-user) - [Password reset - step 1: mail request](#password-reset---step-2-confirmation) - [Password reset - step 2: confirmation](#password-reset---step-2-confirmation) @@ -95,6 +96,7 @@ Output: ``` ...where `` is an [user resource](#user) and `query` contains standard [search query](#search). +Errors: if privileges are too low. Searches for users. @@ -143,7 +145,7 @@ Output: ``` ...where `` is an [user resource](#user). Errors: if such user already exists (names are case insensitive), or either of -user name, password and email are invalid. +user name, password and email are invalid, or privileges are too low. Creates a new user using specified parameters. Names and passwords must match `user_name_regex` and `password_regex` from server's configuration, @@ -174,7 +176,7 @@ Output: Errors: if the user does not exist, or the user with new name already exists (names are case insensitive), or either of user name, password, email or rank are invalid, or the user is trying to update their or someone else's rank to -higher than their own. +higher than their own, or privileges are too low. Updates an existing user using specified parameters. Names and passwords must match `user_name_regex` and `password_regex` from server's configuration, @@ -191,11 +193,22 @@ Output: } ``` ...where `` is an [user resource](#user). -Errors: if the user does not exist. +Errors: if the user does not exist or privileges are too low. Retrieves information about an existing user. +### Removing user +Request: `DELETE /user/` +Output: +```json5 +{} +``` +Errors: if the user does not exist or privileges are too low. + +Deletes existing user. + + ### Password reset - step 1: mail request Request: `GET /password-reset/` Output: diff --git a/server/szurubooru/api/user_api.py b/server/szurubooru/api/user_api.py index a634d974..bae3cb6a 100644 --- a/server/szurubooru/api/user_api.py +++ b/server/szurubooru/api/user_api.py @@ -111,3 +111,19 @@ class UserDetailApi(BaseApi): context.session.commit() return {'user': _serialize_user(context.user, user)} + + def delete(self, context, user_name): + ''' Delete an existing user. ''' + user = users.get_by_name(context.session, user_name) + if not user: + raise errors.NotFoundError('User %r not found.' % user_name) + + if context.user.user_id == user.user_id: + infix = 'self' + else: + infix = 'any' + + auth.verify_privilege(context.user, 'users:delete:%s' % infix) + context.session.delete(user) + context.session.commit() + return {} diff --git a/server/szurubooru/tests/api/test_user_api.py b/server/szurubooru/tests/api/test_user_api.py index bd69df11..5a5dab89 100644 --- a/server/szurubooru/tests/api/test_user_api.py +++ b/server/szurubooru/tests/api/test_user_api.py @@ -10,8 +10,6 @@ class TestRetrievingUsers(DatabaseTestCase): util.mock_config({ 'privileges': { 'users:list': 'regular_user', - 'users:view': 'regular_user', - 'users:create': 'regular_user', }, 'avatar_thumbnail_size': 200, 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'], @@ -39,6 +37,30 @@ class TestRetrievingUsers(DatabaseTestCase): api_ = api.UserListApi() self.assertRaises(errors.AuthError, api_.get, self.context) + def test_retrieving_non_existing(self): + self.context.user.rank = 'regular_user' + util.mock_params(self.context, {'query': 'asd', 'page': 1}) + api_ = api.UserListApi() + result = api_.get(self.context) + self.assertEqual(result['query'], 'asd') + self.assertEqual(result['page'], 1) + self.assertEqual(result['pageSize'], 100) + self.assertEqual(result['total'], 0) + self.assertEqual([u['name'] for u in result['users']], []) + +class TestRetrievingUser(DatabaseTestCase): + def setUp(self): + super().setUp() + util.mock_config({ + 'privileges': { + 'users:view': 'regular_user', + }, + 'avatar_thumbnail_size': 200, + 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'], + 'rank_names': {}, + }) + util.mock_context(self) + def test_retrieving_single(self): user = util.mock_user('u1', 'regular_user') self.session.add(user) @@ -65,6 +87,48 @@ class TestRetrievingUsers(DatabaseTestCase): api_ = api.UserDetailApi() self.assertRaises(errors.AuthError, api_.get, self.context, '-') +class TestDeletingUser(DatabaseTestCase): + def setUp(self): + super().setUp() + util.mock_config({ + 'privileges': { + 'users:delete:self': 'regular_user', + 'users:delete:any': 'mod', + }, + 'ranks': ['anonymous', 'regular_user', 'mod', 'admin'], + 'rank_names': {}, + }) + util.mock_context(self) + + def test_removing_oneself(self): + user1 = util.mock_user('u1', 'regular_user') + user2 = util.mock_user('u2', 'regular_user') + self.session.add_all([user1, user2]) + self.session.commit() + self.context.user.user_id = user1.user_id + self.context.user.rank = 'regular_user' + api_ = api.UserDetailApi() + self.assertRaises(errors.AuthError, api_.delete, self.context, 'u2') + api_.delete(self.context, 'u1') + self.assertEqual(self.session.query(db.User).filter_by(name='u1').all(), []) + + def test_removing_someone_else(self): + user1 = util.mock_user('u1', 'regular_user') + user2 = util.mock_user('u2', 'regular_user') + self.session.add_all([user1, user2]) + self.session.commit() + self.context.user.rank = 'mod' + api_ = api.UserDetailApi() + api_.delete(self.context, 'u1') + api_.delete(self.context, 'u2') + self.assertEqual(self.session.query(db.User).filter_by(name='u1').all(), []) + self.assertEqual(self.session.query(db.User).filter_by(name='u2').all(), []) + + def test_removing_non_existing(self): + self.context.user.rank = 'regular_user' + api_ = api.UserDetailApi() + self.assertRaises(errors.NotFoundError, api_.delete, self.context, 'bad') + class TestCreatingUser(DatabaseTestCase): def setUp(self): super().setUp()