From 0b1664a83c90d6d7bb39e57cf92099ba5b127a4d Mon Sep 17 00:00:00 2001 From: Francesco Siddi Date: Wed, 4 May 2016 17:04:10 +0200 Subject: [PATCH] Add project_manage_users endpoint Manage users of a project. In this initial implementation, we handle addition and removal of a user to the admin group of a project. No changes are done on the project itself. --- pillar/application/modules/projects.py | 45 +++++++++++++++++++++++ tests/test_project_management.py | 50 ++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/pillar/application/modules/projects.py b/pillar/application/modules/projects.py index d00e5771..9882d570 100644 --- a/pillar/application/modules/projects.py +++ b/pillar/application/modules/projects.py @@ -2,6 +2,7 @@ import copy import logging import json +from bson import ObjectId from eve.methods.post import post_internal from eve.methods.patch import patch_internal from flask import g, Blueprint, request, abort, current_app @@ -216,6 +217,50 @@ def create_project(overrides=None): return jsonify(project, status=201, headers={'Location': '/projects/%s' % project['_id']}) +@blueprint.route('/users', methods=['POST']) +@authorization.require_login() +def project_manage_users(): + """Manage users of a project. In this initial implementation, we handle + addition and removal of a user to the admin group of a project. + No changes are done on the project itself. + """ + + project_id = request.form['project_id'] + target_user_id = request.form['user_id'] + action = request.form['action'] + user_id = g.current_user['user_id'] + + projects_collection = current_app.data.driver.db['projects'] + project = projects_collection.find_one({'_id': ObjectId(project_id)}) + # Check if the current_user is owner of the project + # TODO: check based on permissions + if project['user'] != user_id: + return abort_with_error(403) + # Get admin group + # TODO: improve this by checking actual permissions + admin_group_id = project['permissions']['groups'][0]['group'] + # Additional check for admin group (if group name is the same as project id) + groups_collection = current_app.data.driver.db['groups'] + group = groups_collection.find_one({'_id': admin_group_id}) + if group['name'] != project_id: + return abort_with_error(403) + + # Get the user and add the admin group to it + if action == 'add': + operation = '$addToSet' + elif action == 'remove': + operation = '$pull' + else: + return abort_with_error(403) + + users_collection = current_app.data.driver.db['users'] + users_collection.update({'_id': ObjectId(target_user_id)}, + {operation: {'groups': admin_group_id}}) + + # Return the project in the response. + return jsonify({'status': 'success'}) + + def abort_with_error(status): """Aborts with the given status, or 500 if the status doesn't indicate an error. diff --git a/tests/test_project_management.py b/tests/test_project_management.py index e3f0a98e..5b13e56c 100644 --- a/tests/test_project_management.py +++ b/tests/test_project_management.py @@ -302,3 +302,53 @@ class ProjectEditTest(AbstractProjectTest): project = json.loads(resp.data) return project + + def test_add_remove_user(self): + # Create test project + project_info = self._create_user_and_project([u'subscriber']) + project_id = project_info['_id'] + project_add_user_url = '/p/users' + + # Create another user we can try to share the project with + other_user_id = 'f00dd00df00dd00df00dd00d' + self._create_user_with_token(['subscriber'], 'other-token', + user_id=other_user_id) + + # Make request payload + payload = { + 'project_id': project_id, + 'user_id': other_user_id, + 'action': 'add'} + + resp = self.client.post(project_add_user_url, + data=payload, + headers={ + 'Authorization': self.make_header('token'), + 'If-Match': project_info['_etag']}) + self.assertEqual(200, resp.status_code, resp.data) + + with self.app.test_request_context(): + groups = self.app.data.driver.db['groups'] + users = self.app.data.driver.db['users'] + + # Get other_user document + db_user = users.find_one(ObjectId(other_user_id)) + + # Get the admin group (has same name as project id) + # TODO: handle case when user has multiple groups + admin_group_id = db_user['groups'][0] + admin_group = groups.find_one({'_id': admin_group_id}) + + # Check if admin group name matches + self.assertEqual(admin_group['name'], str(project_info['_id'])) + + # Update payload to remove the user we just added + payload['action'] = 'remove' + + resp = self.client.post(project_add_user_url, + data=payload, + headers={ + 'Authorization': self.make_header('token'), + 'If-Match': project_info['_etag']}) + self.assertEqual(200, resp.status_code, resp.data) +