diff --git a/rhodecode/lib/middleware/simplevcs.py b/rhodecode/lib/middleware/simplevcs.py --- a/rhodecode/lib/middleware/simplevcs.py +++ b/rhodecode/lib/middleware/simplevcs.py @@ -238,6 +238,10 @@ class SimpleVCS(object): return False + @property + def is_shadow_repo_dir(self): + return os.path.isdir(self.vcs_repo_name) + def _check_permission(self, action, user, repo_name, ip_addr=None): """ Checks permissions using action (push/pull) user and repository @@ -331,6 +335,11 @@ class SimpleVCS(object): log.debug('User not allowed to proceed, %s', reason) return HTTPNotAcceptable(reason)(environ, start_response) + # Check if the shadow repo actually exists, in case someone refers + # to it, and it has been deleted because of successful merge. + if self.is_shadow_repo and not self.is_shadow_repo_dir: + return HTTPNotFound()(environ, start_response) + # ====================================================================== # CHECK ANONYMOUS PERMISSION # ====================================================================== diff --git a/rhodecode/tests/lib/middleware/test_simplevcs.py b/rhodecode/tests/lib/middleware/test_simplevcs.py --- a/rhodecode/tests/lib/middleware/test_simplevcs.py +++ b/rhodecode/tests/lib/middleware/test_simplevcs.py @@ -45,9 +45,14 @@ class StubVCSController(simplevcs.Simple def __init__(self, *args, **kwargs): super(StubVCSController, self).__init__(*args, **kwargs) self._action = 'pull' + self._is_shadow_repo_dir = True self._name = HG_REPO self.set_repo_names(None) + @property + def is_shadow_repo_dir(self): + return self._is_shadow_repo_dir + def _get_repository_name(self, environ): return self._name @@ -218,6 +223,7 @@ class TestShadowRepoExposure(object): controller._check_ssl = mock.Mock() controller.is_shadow_repo = True controller._action = 'pull' + controller._is_shadow_repo_dir = True controller.stub_response_body = 'dummy body value' environ_stub = { 'HTTP_HOST': 'test.example.com', @@ -232,6 +238,30 @@ class TestShadowRepoExposure(object): # Assert that we got the response from the wsgi app. assert response_body == controller.stub_response_body + def test_pull_on_shadow_repo_that_is_missing(self, pylonsapp): + """ + Check that a pull action to a shadow repo is propagated to the + underlying wsgi app. + """ + controller = StubVCSController(pylonsapp, pylonsapp.config, None) + controller._check_ssl = mock.Mock() + controller.is_shadow_repo = True + controller._action = 'pull' + controller._is_shadow_repo_dir = False + controller.stub_response_body = 'dummy body value' + environ_stub = { + 'HTTP_HOST': 'test.example.com', + 'HTTP_ACCEPT': 'application/mercurial', + 'REQUEST_METHOD': 'GET', + 'wsgi.url_scheme': 'http', + } + + response = controller(environ_stub, mock.Mock()) + response_body = ''.join(response) + + # Assert that we got the response from the wsgi app. + assert '404 Not Found' in response_body + def test_push_on_shadow_repo_raises(self, pylonsapp): """ Check that a push action to a shadow repo is aborted.