changeset 5187:9a23b444a7fe

pullrequests: detect invalid reviewers and raise HTTPBadRequest Normally, the creation of a pullrequest with invalid reviewers is not possible because the list of reviewers is populated from a form element that only shows valid reviewers. However, if creating a pullrequest through an API call, invalid reviewers can be specified but would not be detected. The reviewer would be encoded in the database as 'NULL'/None, and opening such a pull request would cause a server error. Instead, detect invalid reviewers at pullrequest creation/update time and raise HTTPBadRequest.
author Thomas De Schampheleire <thomas.de_schampheleire@alcatel-lucent.com>
date Sun, 14 Jun 2015 13:48:15 +0200
parents 5fb4e6f884ce
children 6db5d95c2d21
files kallithea/controllers/pullrequests.py kallithea/lib/exceptions.py kallithea/model/pull_request.py kallithea/tests/functional/test_pullrequests.py
diffstat 4 files changed, 149 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/kallithea/controllers/pullrequests.py	Thu Jun 11 08:18:14 2015 +0200
+++ b/kallithea/controllers/pullrequests.py	Sun Jun 14 13:48:15 2015 +0200
@@ -44,6 +44,7 @@
 from kallithea.lib.helpers import Page
 from kallithea.lib import helpers as h
 from kallithea.lib import diffs
+from kallithea.lib.exceptions import UserInvalidException
 from kallithea.lib.utils import action_logger, jsonify
 from kallithea.lib.vcs.utils import safe_str
 from kallithea.lib.vcs.exceptions import EmptyRepositoryError
@@ -362,6 +363,9 @@
             Session().commit()
             h.flash(_('Successfully opened new pull request'),
                     category='success')
+        except UserInvalidException, u:
+            h.flash(_('Invalid reviewer "%s" specified') % u, category='error')
+            raise HTTPBadRequest()
         except Exception:
             h.flash(_('Error occurred while creating pull request'),
                     category='error')
@@ -446,6 +450,9 @@
                 old_pull_request.other_repo.repo_name, new_other_ref,
                 revisions, reviewers_ids, title, description
             )
+        except UserInvalidException, u:
+            h.flash(_('Invalid reviewer "%s" specified') % u, category='error')
+            raise HTTPBadRequest()
         except Exception:
             h.flash(_('Error occurred while creating pull request'),
                     category='error')
@@ -495,9 +502,12 @@
         old_description = pull_request.description
         pull_request.title = _form['pullrequest_title']
         pull_request.description = _form['pullrequest_desc'].strip() or _('No description')
-        PullRequestModel().mention_from_description(pull_request, old_description)
-
-        PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
+        try:
+            PullRequestModel().mention_from_description(pull_request, old_description)
+            PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
+        except UserInvalidException, u:
+            h.flash(_('Invalid reviewer "%s" specified') % u, category='error')
+            raise HTTPBadRequest()
 
         Session().commit()
         h.flash(_('Pull request updated'), category='success')
--- a/kallithea/lib/exceptions.py	Thu Jun 11 08:18:14 2015 +0200
+++ b/kallithea/lib/exceptions.py	Sun Jun 14 13:48:15 2015 +0200
@@ -99,6 +99,10 @@
     pass
 
 
+class UserInvalidException(Exception):
+    pass
+
+
 class RepositoryCreationError(Exception):
     pass
 
--- a/kallithea/model/pull_request.py	Thu Jun 11 08:18:14 2015 +0200
+++ b/kallithea/model/pull_request.py	Sun Jun 14 13:48:15 2015 +0200
@@ -32,6 +32,7 @@
 
 from kallithea.model.meta import Session
 from kallithea.lib import helpers as h
+from kallithea.lib.exceptions import UserInvalidException
 from kallithea.model import BaseModel
 from kallithea.model.db import PullRequest, PullRequestReviewers, Notification,\
     ChangesetStatus, User
@@ -117,6 +118,8 @@
         #members
         for member in set(reviewers):
             _usr = self._get_user(member)
+            if _usr is None:
+                raise UserInvalidException(member)
             reviewer = PullRequestReviewers(_usr, pr)
             Session().add(reviewer)
 
--- a/kallithea/tests/functional/test_pullrequests.py	Thu Jun 11 08:18:14 2015 +0200
+++ b/kallithea/tests/functional/test_pullrequests.py	Sun Jun 14 13:48:15 2015 +0200
@@ -1,8 +1,11 @@
+import re
+
 from kallithea.tests import *
 from kallithea.tests.fixture import Fixture
 from kallithea.model.meta import Session
 
 from kallithea.controllers.pullrequests import PullrequestsController
+from kallithea.lib.exceptions import UserInvalidException
 
 fixture = Fixture()
 
@@ -13,6 +16,132 @@
         response = self.app.get(url(controller='pullrequests', action='index',
                                     repo_name=HG_REPO))
 
+    def test_create_trivial(self):
+        self.log_user()
+        response = self.app.post(url(controller='pullrequests', action='create',
+                                     repo_name=HG_REPO),
+                                 {'org_repo': HG_REPO,
+                                  'org_ref': 'branch:default:default',
+                                  'other_repo': HG_REPO,
+                                  'other_ref': 'branch:default:default',
+                                  'pullrequest_title': 'title',
+                                  'pullrequest_desc': 'description',
+                                  '_authentication_token': self.authentication_token(),
+                                 }
+                                )
+        self.assertEqual(response.status, '302 Found')
+        response = response.follow()
+        self.assertEqual(response.status, '200 OK')
+        response.mustcontain('This pull request has already been merged to default.')
+
+    def test_create_with_existing_reviewer(self):
+        self.log_user()
+        response = self.app.post(url(controller='pullrequests', action='create',
+                                     repo_name=HG_REPO),
+                                 {'org_repo': HG_REPO,
+                                  'org_ref': 'branch:default:default',
+                                  'other_repo': HG_REPO,
+                                  'other_ref': 'branch:default:default',
+                                  'pullrequest_title': 'title',
+                                  'pullrequest_desc': 'description',
+                                  '_authentication_token': self.authentication_token(),
+                                  'review_members': TEST_USER_ADMIN_LOGIN,
+                                 }
+                                )
+        self.assertEqual(response.status, '302 Found')
+        response = response.follow()
+        self.assertEqual(response.status, '200 OK')
+        response.mustcontain('This pull request has already been merged to default.')
+
+    def test_create_with_invalid_reviewer(self):
+        invalid_user_name = 'invalid_user'
+        self.log_user()
+        response = self.app.post(url(controller='pullrequests', action='create',
+                                     repo_name=HG_REPO),
+                                 {
+                                  'org_repo': HG_REPO,
+                                  'org_ref': 'branch:default:default',
+                                  'other_repo': HG_REPO,
+                                  'other_ref': 'branch:default:default',
+                                  'pullrequest_title': 'title',
+                                  'pullrequest_desc': 'description',
+                                  '_authentication_token': self.authentication_token(),
+                                  'review_members': invalid_user_name,
+                                 },
+                                 status=400)
+        response.mustcontain('Invalid reviewer &#34;%s&#34; specified' % invalid_user_name)
+
+    def test_update_with_invalid_reviewer(self):
+        invalid_user_id = 99999
+        self.log_user()
+        # create a valid pull request
+        response = self.app.post(url(controller='pullrequests', action='create',
+                                     repo_name=HG_REPO),
+                                 {
+                                  'org_repo': HG_REPO,
+                                  'org_ref': 'branch:default:default',
+                                  'other_repo': HG_REPO,
+                                  'other_ref': 'branch:default:default',
+                                  'pullrequest_title': 'title',
+                                  'pullrequest_desc': 'description',
+                                  '_authentication_token': self.authentication_token(),
+                                 }
+                                )
+        self.assertEqual(response.status, '302 Found')
+        # location is of the form:
+        # http://localhost/vcs_test_hg/pull-request/54/_/title
+        m = re.search('/pull-request/(\d+)/', response.location)
+        self.assertNotEqual(m, None)
+        pull_request_id = m.group(1)
+
+        # update it
+        response = self.app.post(url(controller='pullrequests', action='post',
+                                     repo_name=HG_REPO, pull_request_id=pull_request_id),
+                                 {
+                                  'updaterev': 'default',
+                                  'pullrequest_title': 'title',
+                                  'pullrequest_desc': 'description',
+                                  '_authentication_token': self.authentication_token(),
+                                  'review_members': invalid_user_id,
+                                 },
+                                 status=400)
+        response.mustcontain('Invalid reviewer &#34;%s&#34; specified' % invalid_user_id)
+
+    def test_edit_with_invalid_reviewer(self):
+        invalid_user_id = 99999
+        self.log_user()
+        # create a valid pull request
+        response = self.app.post(url(controller='pullrequests', action='create',
+                                     repo_name=HG_REPO),
+                                 {
+                                  'org_repo': HG_REPO,
+                                  'org_ref': 'branch:default:default',
+                                  'other_repo': HG_REPO,
+                                  'other_ref': 'branch:default:default',
+                                  'pullrequest_title': 'title',
+                                  'pullrequest_desc': 'description',
+                                  '_authentication_token': self.authentication_token(),
+                                 }
+                                )
+        self.assertEqual(response.status, '302 Found')
+        # location is of the form:
+        # http://localhost/vcs_test_hg/pull-request/54/_/title
+        m = re.search('/pull-request/(\d+)/', response.location)
+        self.assertNotEqual(m, None)
+        pull_request_id = m.group(1)
+
+        # edit it
+        response = self.app.post(url(controller='pullrequests', action='post',
+                                     repo_name=HG_REPO, pull_request_id=pull_request_id),
+                                 {
+                                  'pullrequest_title': 'title',
+                                  'pullrequest_desc': 'description',
+                                  '_authentication_token': self.authentication_token(),
+                                  'review_members': invalid_user_id,
+                                 },
+                                 status=400)
+        response.mustcontain('Invalid reviewer &#34;%s&#34; specified' % invalid_user_id)
+
 class TestPullrequestsGetRepoRefs(TestController):
 
     def setUp(self):