From bebac1f97f56302669f3c2abbd6b439c10f1b097 Mon Sep 17 00:00:00 2001 From: Julio Biason Date: Sat, 5 Apr 2014 21:12:36 -0300 Subject: [PATCH] updated documentation and updated the authentication method --- luncho/blueprints/groups.py | 175 ++++++++++++++++++++++++++++++------ tests/group_tests.py | 99 ++++++++++---------- 2 files changed, 196 insertions(+), 78 deletions(-) diff --git a/luncho/blueprints/groups.py b/luncho/blueprints/groups.py index 6a02b97..454bba5 100644 --- a/luncho/blueprints/groups.py +++ b/luncho/blueprints/groups.py @@ -10,7 +10,7 @@ from flask import request from flask import jsonify from luncho.helpers import ForceJSON -from luncho.helpers import user_from_token +from luncho.helpers import auth from luncho.server import User from luncho.server import Group @@ -21,7 +21,15 @@ from luncho.exceptions import ElementNotFoundException class AccountNotVerifiedException(LunchoException): - """The account isn't verified.""" + """The account isn't verified. + + .. sourcecode:: http + + HTTP/1.1 412 Precondition Failed + Content-Type: test/json + + { "status": "ERROR", "message": "Account not verified" } + """ def __init__(self): super(AccountNotVerifiedException, self).__init__() self.status = 412 @@ -29,18 +37,34 @@ class AccountNotVerifiedException(LunchoException): class NewMaintainerDoesNotExistException(LunchoException): - """The account for the new maintainer does not exist.""" + """The account for the new admin does not exist. + + .. sourcecode:: http + + HTTP/1.1 404 Not found + Content-Type: test/json + + { "status": "ERROR", "message": "New admin not found" } + """ def __init__(self): super(NewMaintainerDoesNotExistException, self).__init__() - self.status = 412 - self.message = 'New maintainer not found' + self.status = 404 + self.message = 'New admin not found' class UserIsNotAdminException(LunchoException): - """The user is not the admin of the group.""" + """The user is not the admin of the group. + + .. sourcecode:: http + + HTTP/1.1 403 Forbidden + Content-Type: test/json + + { "status": "ERROR", "message": "User is not admin" } + """ def __init__(self): super(UserIsNotAdminException, self).__init__() - self.status = 401 + self.status = 403 self.message = 'User is not admin' @@ -49,10 +73,32 @@ groups = Blueprint('groups', __name__) LOG = logging.getLogger('luncho.blueprints.groups') -@groups.route('/', methods=['GET']) -def user_groups(token): - """Return a list of the groups the user belongs or it's the owner.""" - user = user_from_token(token) +@groups.route('', methods=['GET']) +@auth +def user_groups(): + """*Authenticated request* Return a list of the groups the user belongs or + it's the owner. + + **Success (200)**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: text/json + + { "status": "OK", "groups": [ { "id": "" , + "name": "", + "admin": }, + ...] } + + **User not found (via token) (404)**: + :py:class:`UserNotFoundException` + + **Authorization required (412)**: + :py:class:`AuthorizationRequiredException` + + """ + user = request.user groups = {} for group in user.groups: groups[group.id] = {'id': group.id, @@ -63,11 +109,30 @@ def user_groups(token): groups=groups.values()) -@groups.route('/', methods=['PUT']) +@groups.route('', methods=['PUT']) @ForceJSON(required=['name']) -def create_group(token): - """Create a new group belonging to the user.""" - user = user_from_token(token) +@auth +def create_group(): + """*Authenticated request* Create a new group. Once the group is created, + the user becomes the administrator of the group. + + **Example request**: + + .. sourcecode:: http + + { "name": "Name for the group" } + + **User not found (via token) (404)**: + :py:class:`UserNotFoundException` + + **Authorization required (412)**: + :py:class:`AuthorizationRequiredException` + + **Account not verified (412)**: + :py:class:`AccountNotVerifiedException` + + """ + user = request.user LOG.debug('User status: {verified}'.format(verified=user.verified)) if not user.verified: @@ -87,11 +152,48 @@ def create_group(token): id=new_group.id) -@groups.route('//', methods=['POST']) +@groups.route('/', methods=['POST']) @ForceJSON() -def update_group(token, groupId): - """Update group information.""" - user = user_from_token(token) +@auth +def update_group(groupId): + """*Authenticated request* Update group information. The user must be + the administrator of the group to change any information. Partial requests + are accepted and missing fields are not changed. + + The administrator of the group can be changed by sending the + "admin" field with the username of the new administrator. + + **Example request**: + + .. sourcecode:: http + + { "name": "new group name": "admin": "newAdmin"} + + **Success (200)**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: text/json + + { "status": "OK" } + + **Request not in JSON format (400)**: + :py:class:`RequestMustBeJSONException` + + **User is not administrator of the group (403)**: + :py:class:`UserIsNotAdminException` + + **User not found (via token) (404)**: + :py:class:`UserNotFoundException` + + **The new admin does not exist (404)**: + :py:class:`NewMaintainerDoesNotExistException` + + **Authorization required (412)**: + :py:class:`AuthorizationRequiredException` + """ + user = request.user group = Group.query.get(groupId) if not group: raise ElementNotFoundException('Group') @@ -105,21 +207,44 @@ def update_group(token, groupId): if 'name' in json: group.name = json['name'] - if 'maintainer' in json: - new_maintainer = User.query.get(json['maintainer']) + if 'admin' in json: + new_maintainer = User.query.get(json['admin']) if not new_maintainer: raise NewMaintainerDoesNotExistException() group.owner = new_maintainer.username + LOG.debug("new owner of {group} = {new_maintainer}".format( + group=group, new_maintainer=new_maintainer)) db.session.commit() return jsonify(status='OK') -@groups.route('//', methods=['DELETE']) -def delete_group(token, groupId): - """Delete a group.""" - user = user_from_token(token) +@groups.route('/', methods=['DELETE']) +@auth +def delete_group(groupId): + """*Authenticated request* Delete a group. Only the administrator of the + group can delete it. + + **Success (200)**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: text/json + + { "status": "OK" } + + **User is not administrator of the group (403)**: + :py:class:`UserIsNotAdminException` + + **User not found (via token) (404)**: + :py:class:`UserNotFoundException` + + **Authorization required (412)**: + :py:class:`AuthorizationRequiredException` + """ + user = request.user group = Group.query.get(groupId) if not group: raise ElementNotFoundException('Group') diff --git a/tests/group_tests.py b/tests/group_tests.py index 2ec20a9..a3d2e12 100644 --- a/tests/group_tests.py +++ b/tests/group_tests.py @@ -28,14 +28,15 @@ class TestGroups(LunchoTests): def test_empty_list(self): """Get an empty list from a user without groups.""" - rv = self.get('/group/{token}/'.format(token=self.user.token)) + rv = self.get('/group/', token=self.user.token) self.assertJsonOk(rv, groups=[]) def test_create_group(self): """Test creating a group.""" request = {'name': 'Test group'} - rv = self.put('/group/{token}/'.format(token=self.user.token), - request) + rv = self.put('/group/', + request, + token=self.user.token) self.assertJsonOk(rv, id=1) def test_create_group_unverified_account(self): @@ -44,22 +45,23 @@ class TestGroups(LunchoTests): server.db.session.commit() request = {'name': 'Test group'} - rv = self.put('/group/{token}/'.format(token=self.user.token), - request) + rv = self.put('/group/', + request, + token=self.user.token) self.assertJsonError(rv, 412, 'Account not verified') def test_user_in_own_group(self): """The user must belong to a group it owns.""" token = self.user.token self.test_create_group() - rv = self.get('/group/{token}/'.format(token=token)) + rv = self.get('/group/', token=token) self.assertJsonOk(rv, groups=[{'id': 1, 'name': 'Test group', 'admin': True}]) def test_get_groups_unknown_token(self): """Request groups with an unknown token.""" - rv = self.get('/group/{token}/'.format(token='invalid')) + rv = self.get('/group/', token='invalid') self.assertJsonError(rv, 404, 'User not found (via token)') def test_get_groups_expired_token(self): @@ -67,14 +69,15 @@ class TestGroups(LunchoTests): self.user.token = 'expired' server.db.session.commit() - rv = self.get('/group/{token}/'.format(token=self.user.token)) + rv = self.get('/group/', token=self.user.token) self.assertJsonError(rv, 400, 'Invalid token') def test_create_group_unknown_token(self): """Try to create a group with an invalid token.""" request = {'name': 'Test group'} - rv = self.put('/group/{token}/'.format(token='invalid'), - request) + rv = self.put('/group/', + request, + token='invalid') self.assertJsonError(rv, 404, 'User not found (via token)') def test_create_group_expired_token(self): @@ -82,8 +85,9 @@ class TestGroups(LunchoTests): server.db.session.commit() request = {'name': 'Test group'} - rv = self.put('/group/{token}/'.format(token=self.user.token), - request) + rv = self.put('/group/', + request, + token=self.user.token) self.assertJsonError(rv, 400, 'Invalid token') @@ -112,9 +116,9 @@ class TestExistingGroups(LunchoTests): """Change the group name.""" groupId = self.group.id request = {'name': 'New test group'} - rv = self.post('/group/{token}/{groupId}/'.format(token=self.user.token, - groupId=self.group.id), - request) + rv = self.post('/group/{groupId}/'.format(groupId=self.group.id), + request, + token=self.user.token) self.assertJsonOk(rv) # check the database @@ -123,11 +127,10 @@ class TestExistingGroups(LunchoTests): def test_update_name_invalid_token(self): """Try to change the name with an unknown token.""" - groupId = self.group.id request = {'name': 'New test group'} - rv = self.post('/group/{token}/{groupId}/'.format(token='invalid', - groupId=self.group.id), - request) + rv = self.post('/group/{groupId}/'.format(groupId=self.group.id), + request, + token='invalid') self.assertJsonError(rv, 404, 'User not found (via token)') def test_update_name_expired_token(self): @@ -136,9 +139,9 @@ class TestExistingGroups(LunchoTests): server.db.session.commit() request = {'name': 'New test group'} - rv = self.post('/group/{token}/{groupId}/'.format(token=self.user.token, - groupId=self.group.id), - request) + rv = self.post('/group/{groupId}/'.format(groupId=self.group.id), + request, + token=self.user.token) self.assertJsonError(rv, 400, 'Invalid token') def test_update_owner(self): @@ -152,11 +155,10 @@ class TestExistingGroups(LunchoTests): groupId = self.group.id new_username = new_user.username - request = {'maintainer': new_user.username} - rv = self.post('/group/{token}/{groupId}/'.format( - token=self.user.token, - groupId=self.group.id), - request) + request = {'admin': new_user.username} + rv = self.post('/group/{groupId}/'.format(groupId=groupId), + request, + token=self.user.token) self.assertJsonOk(rv) # check the database @@ -165,40 +167,33 @@ class TestExistingGroups(LunchoTests): def test_update_owner_invalid(self): """Try to change the owner to a user that doesn't exist.""" - groupId = self.group.id - current_owner = self.group.owner - - request = {'maintainer': 'unknown'} - rv = self.post('/group/{token}/{groupId}/'.format( - token=self.user.token, - groupId=self.group.id - ), request) - self.assertJsonError(rv, 401, 'New maintainer not found') + request = {'admin': 'unknown'} + rv = self.post('/group/{groupId}/'.format(groupId=self.group.id), + request, + token=self.user.token) + self.assertJsonError(rv, 404, 'New admin not found') def test_update_unknown_group(self): """Try to update a group that doesn't exist.""" groupId = self.group.id + 10 request = {'name': 'New test group'} - rv = self.post('/group/{token}/{groupId}/'.format( - token=self.user.token, - groupId=groupId), - request) + rv = self.post('/group/{groupId}/'.format(groupId=groupId), + request, + token=self.user.token) self.assertJsonError(rv, 404, 'Group not found') def test_delete_group(self): """Delete a group.""" groupId = self.group.id - rv = self.delete('/group/{token}/{groupId}/'.format( - token=self.user.token, - groupId=groupId)) + rv = self.delete('/group/{groupId}/'.format(groupId=groupId), + token=self.user.token) self.assertJsonOk(rv) def test_delete_unknown_group(self): """Delete a group that doesn't exist.""" groupId = self.group.id + 10 - rv = self.delete('/group/{token}/{groupId}/'.format( - token=self.user.token, - groupId=groupId)) + rv = self.delete('/group/{groupId}/'.format(groupId=groupId), + token=self.user.token) self.assertJsonError(rv, 404, 'Group not found') def test_delete_not_admin(self): @@ -210,16 +205,14 @@ class TestExistingGroups(LunchoTests): server.db.session.commit() new_user.get_token() - rv = self.delete('/group/{token}/{groupId}/'.format( - token=new_user.token, - groupId=self.group.id)) - self.assertJsonError(rv, 401, 'User is not admin') + rv = self.delete('/group/{groupId}/'.format(groupId=self.group.id), + token=new_user.token) + self.assertJsonError(rv, 403, 'User is not admin') def test_delete_invalid_token(self): """Try to delete a group with an unknown token.""" - rv = self.delete('/group/{token}/{groupId}/'.format( - token='invalid', - groupId=self.group.id)) + rv = self.delete('/group/{groupId}/'.format(groupId=self.group.id), + token='invalid') self.assertJsonError(rv, 404, 'User not found (via token)') if __name__ == '__main__':