diff --git a/rhodecode/model/forms.py b/rhodecode/model/forms.py --- a/rhodecode/model/forms.py +++ b/rhodecode/model/forms.py @@ -242,7 +242,7 @@ def RepoForm(edit=False, old_data=None, allow_extra_fields = True filter_extra_fields = False repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True), - v.SlugifyName()) + v.SlugifyName(), v.CannotHaveGitSuffix()) repo_group = All(v.CanWriteGroup(old_data), v.OneOf(repo_groups, hideList=True)) repo_type = v.OneOf(supported_backends, required=False, diff --git a/rhodecode/model/validation_schema/schemas/repo_schema.py b/rhodecode/model/validation_schema/schemas/repo_schema.py --- a/rhodecode/model/validation_schema/schemas/repo_schema.py +++ b/rhodecode/model/validation_schema/schemas/repo_schema.py @@ -191,7 +191,12 @@ def deferred_unique_name_validator(node, @colander.deferred def deferred_repo_name_validator(node, kw): - return validators.valid_name_validator + def no_git_suffix_validator(node, value): + if value.endswith('.git'): + msg = _('Repository name cannot end with .git') + raise colander.Invalid(node, msg) + return colander.All( + no_git_suffix_validator, validators.valid_name_validator) class GroupType(colander.Mapping): diff --git a/rhodecode/model/validators.py b/rhodecode/model/validators.py --- a/rhodecode/model/validators.py +++ b/rhodecode/model/validators.py @@ -574,6 +574,26 @@ def SlugifyName(): return _validator +def CannotHaveGitSuffix(): + class _validator(formencode.validators.FancyValidator): + messages = { + 'has_git_suffix': + _(u'Repository name cannot end with .git'), + } + + def _to_python(self, value, state): + return value + + def validate_python(self, value, state): + if value and value.endswith('.git'): + msg = M( + self, 'has_git_suffix', state) + raise formencode.Invalid( + msg, value, state, error_dict={'repo_name': msg}) + + return _validator + + def ValidCloneUri(): class InvalidCloneUrl(Exception): allowed_prefixes = () diff --git a/rhodecode/tests/functional/test_admin_repos.py b/rhodecode/tests/functional/test_admin_repos.py --- a/rhodecode/tests/functional/test_admin_repos.py +++ b/rhodecode/tests/functional/test_admin_repos.py @@ -24,7 +24,7 @@ import mock import pytest from rhodecode.lib import auth -from rhodecode.lib.utils2 import safe_str, str2bool +from rhodecode.lib.utils2 import safe_str, str2bool, safe_unicode from rhodecode.lib.vcs.exceptions import RepositoryRequirementError from rhodecode.model.db import Repository, RepoGroup, UserRepoToPerm, User,\ Permission @@ -44,7 +44,7 @@ fixture = Fixture() @pytest.mark.usefixtures("app") -class TestAdminRepos: +class TestAdminRepos(object): def test_index(self): self.app.get(url('repos')) @@ -63,13 +63,14 @@ class TestAdminRepos: assert_response.element_contains('#repo_type', 'svn') assert_response.element_contains('#repo_type', 'hg') - @pytest.mark.parametrize("suffix", [u'', u''], ids=['', 'non-ascii']) + @pytest.mark.parametrize("suffix", + [u'', u'xxa'], ids=['', 'non-ascii']) def test_create(self, autologin_user, backend, suffix, csrf_token): repo_name_unicode = backend.new_repo_name(suffix=suffix) repo_name = repo_name_unicode.encode('utf8') description_unicode = u'description for newly created repo' + suffix description = description_unicode.encode('utf8') - self.app.post( + response = self.app.post( url('repos'), fixture._get_repo_create_params( repo_private=False, @@ -77,8 +78,7 @@ class TestAdminRepos: repo_type=backend.alias, repo_description=description, csrf_token=csrf_token), - status=302 - ) + status=302) self.assert_repository_is_created_correctly( repo_name, description, backend) @@ -368,6 +368,20 @@ class TestAdminRepos: csrf_token=csrf_token)) response.mustcontain('invalid clone url') + def test_create_with_git_suffix( + self, autologin_user, backend, csrf_token): + repo_name = backend.new_repo_name() + ".git" + description = 'description for newly created repo' + response = self.app.post( + url('repos'), + fixture._get_repo_create_params( + repo_private=False, + repo_name=repo_name, + repo_type=backend.alias, + repo_description=description, + csrf_token=csrf_token)) + response.mustcontain('Repository name cannot end with .git') + @pytest.mark.parametrize("suffix", [u'', u'ąęł'], ids=['', 'non-ascii']) def test_delete(self, autologin_user, backend, suffix, csrf_token): repo = backend.create_repo(name_suffix=suffix) @@ -596,15 +610,15 @@ class TestAdminRepos: def assert_repository_is_created_correctly( self, repo_name, description, backend): - repo_name_utf8 = repo_name.encode('utf-8') + repo_name_utf8 = safe_str(repo_name) # run the check page that triggers the flash message response = self.app.get(url('repo_check_home', repo_name=repo_name)) assert response.json == {u'result': True} - assert_session_flash( - response, - u'Created repository %s' - % (urllib.quote(repo_name_utf8), repo_name)) + + flash_msg = u'Created repository {}'.format( + urllib.quote(repo_name_utf8), repo_name) + assert_session_flash(response, flash_msg) # test if the repo was created in the database new_repo = RepoModel().get_by_repo_name(repo_name)