From 489ad10e8b99bf76f79049d5081bf9a4ee5a41d4 Mon Sep 17 00:00:00 2001 From: Matt Prahl Date: Aug 14 2017 09:42:38 +0000 Subject: Add the "git/generateacls" API endpoint for projects --- diff --git a/pagure/api/__init__.py b/pagure/api/__init__.py index 8eab66f..4c5f595 100644 --- a/pagure/api/__init__.py +++ b/pagure/api/__init__.py @@ -438,6 +438,7 @@ def api(): api_new_project_doc = load_doc(project.api_new_project) api_modify_project_doc = load_doc(project.api_modify_project) api_fork_project_doc = load_doc(project.api_fork_project) + api_generate_acls_doc = load_doc(project.api_generate_acls) issues = [] if pagure.APP.config.get('ENABLE_TICKETS', True): @@ -501,7 +502,8 @@ def api(): api_project_git_urls_doc, api_project_watchers_doc, api_git_branches_doc, - api_fork_project_doc + api_fork_project_doc, + api_generate_acls_doc ], issues=issues, requests=[ diff --git a/pagure/api/project.py b/pagure/api/project.py index 67e84a7..acb1802 100644 --- a/pagure/api/project.py +++ b/pagure/api/project.py @@ -1005,3 +1005,84 @@ def api_fork_project(): jsonout = flask.jsonify(output) return jsonout + + +@API.route('//git/generateacls', methods=['POST']) +@API.route('///git/generateacls', methods=['POST']) +@API.route('/fork///git/generateacls', methods=['POST']) +@API.route('/fork////git/generateacls', + methods=['POST']) +@api_login_required(acls=['generate_acls_project']) +@api_method +def api_generate_acls(repo, username=None, namespace=None): + """ + Generate Gitolite ACLs on a project + ----------------------------------- + Generate Gitolite ACLs on a project. This is restricted to Pagure admins. + + This is an asynchronous call. + + :: + + POST /api/0/rpms/python-requests/git/generateacls + + + Input + ^^^^^ + + +------------------+---------+--------------+---------------------------+ + | Key | Type | Optionality | Description | + +==================+=========+==============+===========================+ + | ``wait`` | boolean | Optional | | A boolean to specify if | + | | | | this API call should | + | | | | return a taskid or if it| + | | | | should wait for the task| + | | | | to finish. | + +------------------+---------+--------------+---------------------------+ + + + Sample response + ^^^^^^^^^^^^^^^ + + :: + + wait=False: + { + 'message': 'Project ACL generation queued', + 'taskid': '123-abcd' + } + + wait=True: + { + 'message': 'Project ACLs generated' + } + + """ + project = get_authorized_api_project(SESSION, repo, namespace=namespace) + if not project: + raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOPROJECT) + + # Set force to True to ignore the mimetype. Set silent so that None is + # returned if it's invalid JSON. + json = flask.request.get_json(force=True, silent=True) or {} + wait = json.get('wait', False) + + try: + taskid = pagure.lib.tasks.generate_gitolite_acls.delay( + namespace=namespace, + name=repo, + user=username + ).id + + if wait: + pagure.lib.tasks.get_result(taskid).get() + output = {'message': 'Project ACLs generated'} + else: + output = {'message': 'Project ACL generation queued', + 'taskid': taskid} + except pagure.exceptions.PagureException as err: + raise pagure.exceptions.APIError( + 400, error_code=APIERROR.ENOCODE, error=str(err)) + + jsonout = flask.jsonify(output) + return jsonout diff --git a/pagure/default_config.py b/pagure/default_config.py index cb1aee5..c82da8c 100644 --- a/pagure/default_config.py +++ b/pagure/default_config.py @@ -261,12 +261,13 @@ ACLS = { 'issue_update': 'Update an issue, status, comments, custom fields...', 'issue_update_custom_fields': 'Update the custom fields of an issue', 'issue_update_milestone': 'Update the milestone of an issue', - 'modify_project': 'Modify an existing project' + 'modify_project': 'Modify an existing project', + 'generate_acls_project': 'Generate the Gitolite ACLs on a project' } # List of ACLs which a regular user is allowed to associate to an API token # from the ACLs above -USER_ACLS = ACLS.keys() +USER_ACLS = [key for key in ACLS.keys() if key != 'generate_acls_project'] # From the ACLs above lists which ones are tolerated to be associated with # an API token that isn't linked to a particular project. @@ -284,6 +285,7 @@ ADMIN_API_ACLS = [ 'pull_request_flag', 'pull_request_comment', 'pull_request_merge', + 'generate_acls_project' ] # Bootstrap URLS diff --git a/tests/test_pagure_flask_api_project.py b/tests/test_pagure_flask_api_project.py index c689d9d..3a7b76d 100644 --- a/tests/test_pagure_flask_api_project.py +++ b/tests/test_pagure_flask_api_project.py @@ -20,7 +20,7 @@ import os import pygit2 -from mock import patch +from mock import patch, Mock sys.path.insert(0, os.path.join(os.path.dirname( os.path.abspath(__file__)), '..')) @@ -2104,5 +2104,88 @@ class PagureFlaskApiProjecttests(tests.Modeltests): } ) + @patch('pagure.lib.tasks.generate_gitolite_acls.delay') + def test_api_generate_acls(self, mock_gen_acls): + """ Test the api_generate_acls method of the flask api """ + tests.create_projects(self.session) + tests.create_tokens(self.session, project_id=None) + tests.create_tokens_acl( + self.session, 'aaabbbcccddd', 'generate_acls_project') + headers = {'Authorization': 'token aaabbbcccddd'} + + mock_gen_acls_rv = Mock() + mock_gen_acls_rv.id = 'abc-1234' + mock_gen_acls.return_value = mock_gen_acls_rv + + user = pagure.lib.get_user(self.session, 'pingou') + with tests.user_set(pagure.APP, user): + output = self.app.post( + '/api/0/test/git/generateacls', headers=headers, + data=json.dumps({'wait': False})) + self.assertEqual(output.status_code, 200) + data = json.loads(output.data) + expected_output = { + 'message': 'Project ACL generation queued', + 'taskid': 'abc-1234' + } + self.assertEqual(data, expected_output) + mock_gen_acls.assert_called_once_with( + name='test', namespace=None, user=None) + + @patch('pagure.lib.tasks.get_result') + @patch('pagure.lib.tasks.generate_gitolite_acls.delay') + def test_api_generate_acls_wait_true(self, mock_gen_acls, mock_get_result): + """ Test the api_generate_acls method of the flask api when wait is + set to True """ + tests.create_projects(self.session) + tests.create_tokens(self.session, project_id=None) + tests.create_tokens_acl( + self.session, 'aaabbbcccddd', 'generate_acls_project') + headers = {'Authorization': 'token aaabbbcccddd'} + + mock_gen_acls_rv = Mock() + mock_gen_acls_rv.id = 'abc-1234' + mock_gen_acls.return_value = mock_gen_acls_rv + + mock_get_result_rv = Mock() + mock_get_result.return_value = mock_get_result_rv + + user = pagure.lib.get_user(self.session, 'pingou') + with tests.user_set(pagure.APP, user): + output = self.app.post( + '/api/0/test/git/generateacls', headers=headers, + data=json.dumps({'wait': True})) + self.assertEqual(output.status_code, 200) + data = json.loads(output.data) + expected_output = { + 'message': 'Project ACLs generated', + } + self.assertEqual(data, expected_output) + mock_gen_acls.assert_called_once_with( + name='test', namespace=None, user=None) + mock_get_result.assert_called_once_with('abc-1234') + + def test_api_generate_acls_no_project(self): + """ Test the api_generate_acls method of the flask api when the project + doesn't exist """ + tests.create_projects(self.session) + tests.create_tokens(self.session, project_id=None) + tests.create_tokens_acl( + self.session, 'aaabbbcccddd', 'generate_acls_project') + headers = {'Authorization': 'token aaabbbcccddd'} + + user = pagure.lib.get_user(self.session, 'pingou') + with tests.user_set(pagure.APP, user): + output = self.app.post( + '/api/0/test12345123/git/generateacls', headers=headers, + data=json.dumps({'wait': False})) + self.assertEqual(output.status_code, 404) + data = json.loads(output.data) + expected_output = { + 'error_code': 'ENOPROJECT', + 'error': 'Project not found' + } + self.assertEqual(data, expected_output) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/tests/test_pagure_flask_ui_repo.py b/tests/test_pagure_flask_ui_repo.py index 50327f2..92843ba 100644 --- a/tests/test_pagure_flask_ui_repo.py +++ b/tests/test_pagure_flask_ui_repo.py @@ -3893,7 +3893,7 @@ index 0000000..fb7093d self.assertIn('Create a new token', output.data) self.assertEqual( output.data.count('