diff --git a/rhodecode/__init__.py b/rhodecode/__init__.py --- a/rhodecode/__init__.py +++ b/rhodecode/__init__.py @@ -51,7 +51,7 @@ PYRAMID_SETTINGS = {} EXTENSIONS = {} __version__ = ('.'.join((str(each) for each in VERSION[:3]))) -__dbversion__ = 90 # defines current db version for migrations +__dbversion__ = 91 # defines current db version for migrations __platform__ = platform.system() __license__ = 'AGPLv3, and Commercial License' __author__ = 'RhodeCode GmbH' diff --git a/rhodecode/apps/_base/__init__.py b/rhodecode/apps/_base/__init__.py --- a/rhodecode/apps/_base/__init__.py +++ b/rhodecode/apps/_base/__init__.py @@ -505,6 +505,36 @@ class RepoRoutePredicate(object): return False +class RepoForbidArchivedRoutePredicate(object): + def __init__(self, val, config): + self.val = val + + def text(self): + return 'repo_forbid_archived = %s' % self.val + + phash = text + + def __call__(self, info, request): + _ = request.translate + rhodecode_db_repo = request.db_repo + + log.debug( + '%s checking if archived flag for repo for %s', + self.__class__.__name__, rhodecode_db_repo.repo_name) + + if rhodecode_db_repo.archived: + log.warning('Current view is not supported for archived repo:%s', + rhodecode_db_repo.repo_name) + + h.flash( + h.literal(_('Action not supported for archived repository.')), + category='warning') + summary_url = request.route_path( + 'repo_summary', repo_name=rhodecode_db_repo.repo_name) + raise HTTPFound(summary_url) + return True + + class RepoTypeRoutePredicate(object): def __init__(self, val, config): self.val = val or ['hg', 'git', 'svn'] @@ -530,13 +560,6 @@ class RepoTypeRoutePredicate(object): else: log.warning('Current view is not supported for repo type:%s', rhodecode_db_repo.repo_type) - - # h.flash(h.literal( - # _('Action not supported for %s.' % rhodecode_repo.alias)), - # category='warning') - # return redirect( - # route_path('repo_summary', repo_name=cls.rhodecode_db_repo.repo_name)) - return False @@ -643,10 +666,12 @@ def includeme(config): config.add_route_predicate( 'repo_accepted_types', RepoTypeRoutePredicate) config.add_route_predicate( + 'repo_forbid_when_archived', RepoForbidArchivedRoutePredicate) + config.add_route_predicate( 'repo_group_route', RepoGroupRoutePredicate) config.add_route_predicate( 'user_group_route', UserGroupRoutePredicate) config.add_route_predicate( 'user_route_with_default', UserRouteWithDefaultPredicate) config.add_route_predicate( - 'user_route', UserRoutePredicate) \ No newline at end of file + 'user_route', UserRoutePredicate) diff --git a/rhodecode/apps/home/views.py b/rhodecode/apps/home/views.py --- a/rhodecode/apps/home/views.py +++ b/rhodecode/apps/home/views.py @@ -33,7 +33,7 @@ from rhodecode.lib.index import searcher from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int from rhodecode.lib.ext_json import json from rhodecode.model.db import ( - func, or_, in_filter_generator, Repository, RepoGroup, User, UserGroup) + func, true, or_, in_filter_generator, Repository, RepoGroup, User, UserGroup) from rhodecode.model.repo import RepoModel from rhodecode.model.repo_group import RepoGroupModel from rhodecode.model.scm import RepoGroupList, RepoList @@ -114,6 +114,7 @@ class HomeView(BaseAppView): query = Repository.query()\ .order_by(func.length(Repository.repo_name))\ .order_by(Repository.repo_name)\ + .filter(Repository.archived.isnot(true()))\ .filter(or_( # generate multiple IN to fix limitation problems *in_filter_generator(Repository.repo_id, allowed_ids) diff --git a/rhodecode/apps/repository/__init__.py b/rhodecode/apps/repository/__init__.py --- a/rhodecode/apps/repository/__init__.py +++ b/rhodecode/apps/repository/__init__.py @@ -231,11 +231,13 @@ def includeme(config): config.add_route( name='repo_fork_new', pattern='/{repo_name:.*?[^/]}/fork', repo_route=True, + repo_forbid_when_archived=True, repo_accepted_types=['hg', 'git']) config.add_route( name='repo_fork_create', pattern='/{repo_name:.*?[^/]}/fork/create', repo_route=True, + repo_forbid_when_archived=True, repo_accepted_types=['hg', 'git']) config.add_route( @@ -276,27 +278,29 @@ def includeme(config): config.add_route( name='pullrequest_new', pattern='/{repo_name:.*?[^/]}/pull-request/new', - repo_route=True, repo_accepted_types=['hg', 'git']) + repo_route=True, repo_accepted_types=['hg', 'git'], + repo_forbid_when_archived=True) config.add_route( name='pullrequest_create', pattern='/{repo_name:.*?[^/]}/pull-request/create', - repo_route=True, repo_accepted_types=['hg', 'git']) + repo_route=True, repo_accepted_types=['hg', 'git'], + repo_forbid_when_archived=True) config.add_route( name='pullrequest_update', pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/update', - repo_route=True) + repo_route=True, repo_forbid_when_archived=True) config.add_route( name='pullrequest_merge', pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/merge', - repo_route=True) + repo_route=True, repo_forbid_when_archived=True) config.add_route( name='pullrequest_delete', pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/delete', - repo_route=True) + repo_route=True, repo_forbid_when_archived=True) config.add_route( name='pullrequest_comment_create', @@ -319,6 +323,9 @@ def includeme(config): name='edit_repo_advanced', pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True) config.add_route( + name='edit_repo_advanced_archive', + pattern='/{repo_name:.*?[^/]}/settings/advanced/archive', repo_route=True) + config.add_route( name='edit_repo_advanced_delete', pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True) config.add_route( diff --git a/rhodecode/apps/repository/tests/test_repo_forks.py b/rhodecode/apps/repository/tests/test_repo_forks.py --- a/rhodecode/apps/repository/tests/test_repo_forks.py +++ b/rhodecode/apps/repository/tests/test_repo_forks.py @@ -303,6 +303,27 @@ class TestRepoForkViewTests(TestControll assert response.json == {u'data': [], u'draw': None, u'recordsFiltered': 0, u'recordsTotal': 0} + @pytest.mark.parametrize('url_type', [ + 'repo_fork_new', + 'repo_fork_create' + ]) + def test_fork_is_forbidden_on_archived_repo(self, backend, xhr_header, user_util, url_type): + user = user_util.create_user(password='qweqwe') + self.log_user(user.username, 'qweqwe') + + # create a temporary repo + source = user_util.create_repo(repo_type=backend.alias) + repo_name = source.repo_name + repo = Repository.get_by_repo_name(repo_name) + repo.archived = True + Session().commit() + + response = self.app.get( + route_path(url_type, repo_name=repo_name), status=302) + + msg = 'Action not supported for archived repository.' + assert_session_flash(response, msg) + class TestSVNFork(TestController): @pytest.mark.parametrize('route_name', [ diff --git a/rhodecode/apps/repository/tests/test_repo_pullrequests.py b/rhodecode/apps/repository/tests/test_repo_pullrequests.py --- a/rhodecode/apps/repository/tests/test_repo_pullrequests.py +++ b/rhodecode/apps/repository/tests/test_repo_pullrequests.py @@ -26,7 +26,7 @@ from rhodecode.lib.vcs.nodes import File from rhodecode.lib import helpers as h from rhodecode.model.changeset_status import ChangesetStatusModel from rhodecode.model.db import ( - PullRequest, ChangesetStatus, UserLog, Notification, ChangesetComment) + PullRequest, ChangesetStatus, UserLog, Notification, ChangesetComment, Repository) from rhodecode.model.meta import Session from rhodecode.model.pull_request import PullRequestModel from rhodecode.model.user import UserModel @@ -1191,6 +1191,28 @@ class TestPullrequestsControllerDelete(o ) assert response.body == 'true' + @pytest.mark.parametrize('url_type', [ + 'pullrequest_new', + 'pullrequest_create', + 'pullrequest_update', + 'pullrequest_merge', + ]) + def test_pull_request_is_forbidden_on_archived_repo( + self, autologin_user, backend, xhr_header, user_util, url_type): + + # create a temporary repo + source = user_util.create_repo(repo_type=backend.alias) + repo_name = source.repo_name + repo = Repository.get_by_repo_name(repo_name) + repo.archived = True + Session().commit() + + response = self.app.get( + route_path(url_type, repo_name=repo_name, pull_request_id=1), status=302) + + msg = 'Action not supported for archived repository.' + assert_session_flash(response, msg) + def assert_pull_request_status(pull_request, expected_status): status = ChangesetStatusModel().calculated_review_status( diff --git a/rhodecode/apps/repository/tests/test_repo_settings_advanced.py b/rhodecode/apps/repository/tests/test_repo_settings_advanced.py --- a/rhodecode/apps/repository/tests/test_repo_settings_advanced.py +++ b/rhodecode/apps/repository/tests/test_repo_settings_advanced.py @@ -39,6 +39,7 @@ def route_path(name, params=None, **kwar 'repo_summary': '/{repo_name}', 'edit_repo_advanced': '/{repo_name}/settings/advanced', 'edit_repo_advanced_delete': '/{repo_name}/settings/advanced/delete', + 'edit_repo_advanced_archive': '/{repo_name}/settings/advanced/archive', 'edit_repo_advanced_fork': '/{repo_name}/settings/advanced/fork', 'edit_repo_advanced_locking': '/{repo_name}/settings/advanced/locking', 'edit_repo_advanced_journal': '/{repo_name}/settings/advanced/journal', @@ -133,7 +134,7 @@ class TestAdminRepoSettingsAdvanced(obje "suffix", ['', u'ąęł' , '123'], ids=no_newline_id_generator) - def test_advanced_delete(self, autologin_user, backend, suffix, csrf_token): + def test_advanced_repo_delete(self, autologin_user, backend, suffix, csrf_token): repo = backend.create_repo(name_suffix=suffix) repo_name = repo.repo_name repo_name_str = safe_str(repo.repo_name) @@ -148,3 +149,25 @@ class TestAdminRepoSettingsAdvanced(obje # check if repo was deleted from db assert RepoModel().get_by_repo_name(repo_name) is None assert not repo_on_filesystem(repo_name_str) + + @pytest.mark.parametrize( + "suffix", + ['', u'ąęł' , '123'], + ids=no_newline_id_generator) + def test_advanced_repo_archive(self, autologin_user, backend, suffix, csrf_token): + repo = backend.create_repo(name_suffix=suffix) + repo_name = repo.repo_name + repo_name_str = safe_str(repo.repo_name) + + response = self.app.post( + route_path('edit_repo_advanced_archive', repo_name=repo_name_str), + params={'csrf_token': csrf_token}) + + assert_session_flash(response, + u'Archived repository `{}`'.format(repo_name)) + + response = self.app.get(route_path('repo_summary', repo_name=repo_name_str)) + response.mustcontain('This repository has been archived. It is now read-only.') + + # check if repo was deleted from db + assert RepoModel().get_by_repo_name(repo_name).archived is True diff --git a/rhodecode/apps/repository/views/repo_settings_advanced.py b/rhodecode/apps/repository/views/repo_settings_advanced.py --- a/rhodecode/apps/repository/views/repo_settings_advanced.py +++ b/rhodecode/apps/repository/views/repo_settings_advanced.py @@ -23,6 +23,7 @@ import logging from pyramid.view import view_config from pyramid.httpexceptions import HTTPFound +from rhodecode import events from rhodecode.apps._base import RepoAppView from rhodecode.lib import helpers as h from rhodecode.lib import audit_logger @@ -45,6 +46,13 @@ class RepoSettingsView(RepoAppView): c = self._get_local_tmpl_context() return c + def _get_users_with_permissions(self): + user_permissions = {} + for perm in self.db_repo.permissions(): + user_permissions[perm.user_id] = perm + + return user_permissions + @LoginRequired() @HasRepoPermissionAnyDecorator('repository.admin') @view_config( @@ -71,6 +79,49 @@ class RepoSettingsView(RepoAppView): @HasRepoPermissionAnyDecorator('repository.admin') @CSRFRequired() @view_config( + route_name='edit_repo_advanced_archive', request_method='POST', + renderer='rhodecode:templates/admin/repos/repo_edit.mako') + def edit_advanced_archive(self): + """ + Archives the repository. It will become read-only, and not visible in search + or other queries. But still visible for super-admins. + """ + + _ = self.request.translate + + try: + old_data = self.db_repo.get_api_data() + RepoModel().archive(self.db_repo) + + repo = audit_logger.RepoWrap(repo_id=None, repo_name=self.db_repo.repo_name) + audit_logger.store_web( + 'repo.archive', action_data={'old_data': old_data}, + user=self._rhodecode_user, repo=repo) + + ScmModel().mark_for_invalidation(self.db_repo_name, delete=True) + h.flash( + _('Archived repository `%s`') % self.db_repo_name, + category='success') + Session().commit() + except Exception: + log.exception("Exception during archiving of repository") + h.flash(_('An error occurred during archiving of `%s`') + % self.db_repo_name, category='error') + # redirect to advanced for more deletion options + raise HTTPFound( + h.route_path('edit_repo_advanced', repo_name=self.db_repo_name, + _anchor='advanced-archive')) + + # flush permissions for all users defined in permissions + affected_user_ids = self._get_users_with_permissions().keys() + events.trigger(events.UserPermissionsChange(affected_user_ids)) + + raise HTTPFound(h.route_path('home')) + + @LoginRequired() + @HasRepoPermissionAnyDecorator('repository.admin') + @CSRFRequired() + @view_config( route_name='edit_repo_advanced_delete', request_method='POST', renderer='rhodecode:templates/admin/repos/repo_edit.mako') def edit_advanced_delete(self): diff --git a/rhodecode/lib/audit_logger.py b/rhodecode/lib/audit_logger.py --- a/rhodecode/lib/audit_logger.py +++ b/rhodecode/lib/audit_logger.py @@ -65,6 +65,7 @@ ACTIONS_V1 = { 'repo.edit': {'old_data': {}}, 'repo.edit.permissions': {}, 'repo.edit.permissions.branch': {}, + 'repo.archive': {'old_data': {}}, 'repo.delete': {'old_data': {}}, 'repo.archive.download': {'user_agent': '', 'archive_name': '', diff --git a/rhodecode/lib/auth.py b/rhodecode/lib/auth.py --- a/rhodecode/lib/auth.py +++ b/rhodecode/lib/auth.py @@ -322,6 +322,7 @@ def _cached_perms_data(user_id, scope, u class PermOrigin(object): SUPER_ADMIN = 'superadmin' + ARCHIVED = 'archived' REPO_USER = 'user:%s' REPO_USERGROUP = 'usergroup:%s' @@ -463,8 +464,14 @@ class PermissionCalculator(object): # repositories for perm in self.default_repo_perms: r_k = perm.UserRepoToPerm.repository.repo_name + archived = perm.UserRepoToPerm.repository.archived p = 'repository.admin' self.permissions_repositories[r_k] = p, PermOrigin.SUPER_ADMIN + # special case for archived repositories, which we block still even for + # super admins + if archived: + p = 'repository.read' + self.permissions_repositories[r_k] = p, PermOrigin.ARCHIVED # repository groups for perm in self.default_repo_groups_perms: @@ -572,6 +579,7 @@ class PermissionCalculator(object): def _calculate_default_permissions_repositories(self, user_inherit_object_permissions): for perm in self.default_repo_perms: r_k = perm.UserRepoToPerm.repository.repo_name + archived = perm.UserRepoToPerm.repository.archived p = perm.Permission.permission_name o = PermOrigin.REPO_DEFAULT self.permissions_repositories[r_k] = p, o @@ -602,6 +610,15 @@ class PermissionCalculator(object): o = PermOrigin.SUPER_ADMIN self.permissions_repositories[r_k] = p, o + # finally in case of archived repositories, we downgrade higher + # permissions to read + if archived: + current_perm = self.permissions_repositories[r_k] + if current_perm in ['repository.write', 'repository.admin']: + p = 'repository.read' + o = PermOrigin.ARCHIVED + self.permissions_repositories[r_k] = p, o + def _calculate_default_permissions_repository_branches(self, user_inherit_object_permissions): for perm in self.default_branch_repo_perms: diff --git a/rhodecode/lib/dbmigrate/versions/091_version_4_14_0.py b/rhodecode/lib/dbmigrate/versions/091_version_4_14_0.py new file mode 100644 --- /dev/null +++ b/rhodecode/lib/dbmigrate/versions/091_version_4_14_0.py @@ -0,0 +1,36 @@ +import logging + +from sqlalchemy import * + +from rhodecode.model import meta +from rhodecode.lib.dbmigrate.versions import _reset_base, notify + +log = logging.getLogger(__name__) + + +def upgrade(migrate_engine): + """ + Upgrade operations go here. + Don't create your own engine; bind migrate_engine to your metadata + """ + _reset_base(migrate_engine) + from rhodecode.lib.dbmigrate.schema import db_4_13_0_0 as db + + repository_table = db.Repository.__table__ + + archived = Column('archived', Boolean(), nullable=True) + archived.create(table=repository_table) + + # issue fixups + fixups(db, meta.Session) + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + +def fixups(models, _SESSION): + pass + + diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -1585,6 +1585,8 @@ class Repository(Base, BaseModel): unique=False, default=None) private = Column( "private", Boolean(), nullable=True, unique=None, default=None) + archived = Column( + "archived", Boolean(), nullable=True, unique=None, default=None) enable_statistics = Column( "statistics", Boolean(), nullable=True, unique=None, default=True) enable_downloads = Column( @@ -1783,9 +1785,12 @@ class Repository(Base, BaseModel): @classmethod def get_all_repos(cls, user_id=Optional(None), group_id=Optional(None), - case_insensitive=True): + case_insensitive=True, archived=False): q = Repository.query() + if not archived: + q = q.filter(Repository.archived.isnot(true())) + if not isinstance(user_id, Optional): q = q.filter(Repository.user_id == user_id) @@ -1796,6 +1801,7 @@ class Repository(Base, BaseModel): q = q.order_by(func.lower(Repository.repo_name)) else: q = q.order_by(Repository.repo_name) + return q.all() @property diff --git a/rhodecode/model/repo.py b/rhodecode/model/repo.py --- a/rhodecode/model/repo.py +++ b/rhodecode/model/repo.py @@ -207,8 +207,8 @@ class RepoModel(BaseModel): def quick_menu(repo_name): return _render('quick_menu', repo_name) - def repo_lnk(name, rtype, rstate, private, fork_of): - return _render('repo_name', name, rtype, rstate, private, fork_of, + def repo_lnk(name, rtype, rstate, private, archived, fork_of): + return _render('repo_name', name, rtype, rstate, private, archived, fork_of, short_name=not admin, admin=False) def last_change(last_change): @@ -246,8 +246,8 @@ class RepoModel(BaseModel): row = { "menu": quick_menu(repo.repo_name), - "name": repo_lnk(repo.repo_name, repo.repo_type, - repo.repo_state, repo.private, repo.fork), + "name": repo_lnk(repo.repo_name, repo.repo_type, repo.repo_state, + repo.private, repo.archived, repo.fork), "name_raw": repo.repo_name.lower(), "last_change": last_change(repo.last_db_change), @@ -427,6 +427,7 @@ class RepoModel(BaseModel): new_repo.group = repo_group new_repo.description = description or repo_name new_repo.private = private + new_repo.archived = False new_repo.clone_uri = clone_uri new_repo.landing_rev = landing_rev @@ -608,6 +609,23 @@ class RepoModel(BaseModel): from rhodecode.lib.celerylib import tasks, run_task return run_task(tasks.create_repo_fork, form_data, cur_user) + def archive(self, repo): + """ + Archive given repository. Set archive flag. + + :param repo: + """ + repo = self._get_repo(repo) + if repo: + + try: + repo.archived = True + self.sa.add(repo) + self.sa.commit() + except Exception: + log.error(traceback.format_exc()) + raise + def delete(self, repo, forks=None, pull_requests=None, fs_remove=True, cur_user=None): """ Delete given repository, forks parameter defines what do do with @@ -616,6 +634,7 @@ class RepoModel(BaseModel): :param repo: :param forks: str 'delete' or 'detach' + :param pull_requests: str 'delete' or None :param fs_remove: remove(archive) repo from filesystem """ if not cur_user: diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js --- a/rhodecode/public/js/rhodecode/routes.js +++ b/rhodecode/public/js/rhodecode/routes.js @@ -228,6 +228,7 @@ function registerRCRoutes() { pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']); pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']); pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']); + pyroutes.register('edit_repo_advanced_archive', '/%(repo_name)s/settings/advanced/archive', ['repo_name']); pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']); pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']); pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']); diff --git a/rhodecode/templates/admin/repos/repo_edit_advanced.mako b/rhodecode/templates/admin/repos/repo_edit_advanced.mako --- a/rhodecode/templates/admin/repos/repo_edit_advanced.mako +++ b/rhodecode/templates/admin/repos/repo_edit_advanced.mako @@ -98,7 +98,7 @@ %endif @@ -111,6 +111,35 @@ + +
+
+

${_('Archive repository')}

+
+
+ ${h.secure_form(h.route_path('edit_repo_advanced_archive', repo_name=c.repo_name), request=request)} + +
+ +
+ +
+
+ + ${_('Archiving the repository will make it entirely read-only. The repository cannot be committed to.' + 'It is hidden from the search results and dashboard. ')} + +
+ + ${h.end_form()} +
+
+ +

${_('Delete repository')}

@@ -152,7 +181,7 @@
diff --git a/rhodecode/templates/base/base.mako b/rhodecode/templates/base/base.mako --- a/rhodecode/templates/base/base.mako +++ b/rhodecode/templates/base/base.mako @@ -287,6 +287,11 @@
+ % if c.rhodecode_db_repo.archived: +
+ ${_('This repository has been archived. It is now read-only.')} +
+ % endif diff --git a/rhodecode/templates/data_table/_dt_elements.mako b/rhodecode/templates/data_table/_dt_elements.mako --- a/rhodecode/templates/data_table/_dt_elements.mako +++ b/rhodecode/templates/data_table/_dt_elements.mako @@ -66,7 +66,7 @@ -<%def name="repo_name(name,rtype,rstate,private,fork_of,short_name=False,admin=False)"> +<%def name="repo_name(name,rtype,rstate,private,archived,fork_of,short_name=False,admin=False)"> <% def get_name(name,short_name=short_name): if short_name: @@ -105,6 +105,7 @@ (${_('creating...')}) %endif + diff --git a/rhodecode/tests/lib/test_libs.py b/rhodecode/tests/lib/test_libs.py --- a/rhodecode/tests/lib/test_libs.py +++ b/rhodecode/tests/lib/test_libs.py @@ -561,7 +561,7 @@ def test_get_repo_by_id(test, expected): def test_invalidation_context(baseapp): - repo_id = 999 + repo_id = 9999 cache_namespace_uid = 'cache_repo_instance.{}_{}'.format( repo_id, CacheKey.CACHE_TYPE_README) diff --git a/rhodecode/tests/vcs_operations/test_vcs_operations_special.py b/rhodecode/tests/vcs_operations/test_vcs_operations_special.py --- a/rhodecode/tests/vcs_operations/test_vcs_operations_special.py +++ b/rhodecode/tests/vcs_operations/test_vcs_operations_special.py @@ -25,10 +25,13 @@ import pytest from rhodecode.lib.vcs.backends.git.repository import GitRepository from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository from rhodecode.lib.vcs.nodes import FileNode +from rhodecode.model.db import Repository from rhodecode.model.meta import Session +from rhodecode.tests import GIT_REPO, HG_REPO from rhodecode.tests.vcs_operations import ( - Command, _check_proper_clone, _check_proper_git_push, _check_proper_hg_push) + Command, _check_proper_clone, _check_proper_git_push, _check_proper_hg_push, + _add_files_and_push) @pytest.mark.usefixtures("disable_locking") @@ -242,3 +245,37 @@ class TestVCSOperationsSpecial(object): assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stdout assert 'remote: RhodeCode: push completed' in stdout assert 'exporting bookmark feature2' in stdout + + def test_push_is_forbidden_on_archived_repo_hg(self, backend_hg, rc_web_server, tmpdir): + empty_repo = backend_hg.create_repo() + repo_name = empty_repo.repo_name + + repo = Repository.get_by_repo_name(repo_name) + repo.archived = True + Session().commit() + + clone_url = rc_web_server.repo_clone_url(repo_name) + stdout, stderr = Command('/tmp').execute( + 'hg clone', clone_url, tmpdir.strpath) + + stdout, stderr = _add_files_and_push( + 'hg', tmpdir.strpath, clone_url=clone_url) + + assert 'abort: HTTP Error 403: Forbidden' in stderr + + def test_push_is_forbidden_on_archived_repo_git(self, backend_git, rc_web_server, tmpdir): + empty_repo = backend_git.create_repo() + repo_name = empty_repo.repo_name + + repo = Repository.get_by_repo_name(repo_name) + repo.archived = True + Session().commit() + + clone_url = rc_web_server.repo_clone_url(repo_name) + stdout, stderr = Command('/tmp').execute( + 'git clone', clone_url, tmpdir.strpath) + + stdout, stderr = _add_files_and_push( + 'git', tmpdir.strpath, clone_url=clone_url) + + assert "The requested URL returned error: 403" in stderr