From 0030f0692ae20cb3d3a4c624732c8e03f4cdd423 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jun 02 2015 13:21:36 +0000 Subject: [PATCH 1/3] If the user provided a token, validate it even on optional endpoints optional token endpoints are endpoints where providing a token is not mandatory. For examples to list tickets, if you are anonymous (no token), it will return all the public tickets while if you are logged in, it will also return the private tickets you have access to. But for the API's consistency, as soon as the user provides a token we should check if it is still valid and signal if it is not --- diff --git a/pagure/api/__init__.py b/pagure/api/__init__.py index 5f50546..146920c 100644 --- a/pagure/api/__init__.py +++ b/pagure/api/__init__.py @@ -77,6 +77,8 @@ def check_api_acls(acls, optional=False): token_auth = True flask.g.fas_user = token.user flask.g.token = token + elif optional: + return if not token_auth: output = { @@ -122,7 +124,9 @@ def api_login_optional(acls=None): def decorated_function(*args, **kwargs): ''' Actually does the job with the arguments provided. ''' - check_api_acls(acls, optional=True) + response = check_api_acls(acls, optional=True) + if response: + return response return fn(*args, **kwargs) return decorated_function From 9bfb349248b708a686fcd6ffd432e33523d56382 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jun 02 2015 13:25:09 +0000 Subject: [PATCH 2/3] Check if the token is for this project, even on optional token API endpoints Optional API endpoints do not require an API token to be queried, but if the user provides one, we should make sure it is a token that is valid for this project. --- diff --git a/pagure/api/issue.py b/pagure/api/issue.py index 1e3e5ee..ec50716 100644 --- a/pagure/api/issue.py +++ b/pagure/api/issue.py @@ -269,6 +269,9 @@ def api_view_issues(repo, username=None): private = False # If user is authenticated, show him/her his/her private tickets if authenticated(): + if repo != flask.g.token.project: + raise pagure.exceptions.APIError( + 401, error_code=APIERROR.EINVALIDTOK) private = flask.g.fas_user.username # If user is repo admin, show all tickets included the private ones if is_repo_admin(repo): @@ -370,6 +373,11 @@ def api_view_issue(repo, issueid, username=None): if issue is None or issue.project != repo: raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOISSUE) + if authenticated(): + if repo != flask.g.token.project: + raise pagure.exceptions.APIError( + 401, error_code=APIERROR.EINVALIDTOK) + if issue.private and not is_repo_admin(repo) \ and (not authenticated() or not issue.user.user == flask.g.fas_user.username): From c4fef57ebc51cfae55d36d39a154159478fce692 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jun 02 2015 13:26:59 +0000 Subject: [PATCH 3/3] Adjust the unit-tests for the change in behavior on the optional token API --- diff --git a/tests/test_progit_flask_api_issue.py b/tests/test_progit_flask_api_issue.py index bab7875..fc91fab 100644 --- a/tests/test_progit_flask_api_issue.py +++ b/tests/test_progit_flask_api_issue.py @@ -228,6 +228,22 @@ class PagureFlaskApiIssuetests(tests.Modeltests): ) headers = {'Authorization': 'token aaabbbccc'} + # Access issues authenticated but non-existing token + output = self.app.get('/api/0/test/issues', headers=headers) + self.assertEqual(output.status_code, 401) + + # Create a new token for another user + item = pagure.lib.model.Token( + id='bar_token', + user_id=2, + project_id=1, + expiration=datetime.datetime.utcnow() + datetime.timedelta( + days=30) + ) + self.session.add(item) + + headers = {'Authorization': 'token bar_token'} + # Access issues authenticated but wrong token output = self.app.get('/api/0/test/issues', headers=headers) self.assertEqual(output.status_code, 200) @@ -436,6 +452,30 @@ class PagureFlaskApiIssuetests(tests.Modeltests): headers = {'Authorization': 'token aaabbbccc'} + # Access private issue authenticated but non-existing token + output = self.app.get('/api/0/test/issue/2', headers=headers) + self.assertEqual(output.status_code, 401) + data = json.loads(output.data) + self.assertDictEqual( + data, + { + "error": "Invalid or expired token. Please visit https://pagure.org/ to get or renew your API token.", + "error_code": "EINVALIDTOK" + } + ) + + # Create a new token for another user + item = pagure.lib.model.Token( + id='bar_token', + user_id=2, + project_id=1, + expiration=datetime.datetime.utcnow() + datetime.timedelta( + days=30) + ) + self.session.add(item) + + headers = {'Authorization': 'token bar_token'} + # Access private issue authenticated but wrong token output = self.app.get('/api/0/test/issue/2', headers=headers) self.assertEqual(output.status_code, 403)