diff --git a/scm-ui/ui-webapp/src/repos/components/form/RepositoryForm.tsx b/scm-ui/ui-webapp/src/repos/components/form/RepositoryForm.tsx index 091bfdeee8..a841715104 100644 --- a/scm-ui/ui-webapp/src/repos/components/form/RepositoryForm.tsx +++ b/scm-ui/ui-webapp/src/repos/components/form/RepositoryForm.tsx @@ -268,7 +268,7 @@ class RepositoryForm extends React.Component { handleNamespaceChange = (namespace: string) => { this.setState({ - namespaceValidationError: !validator.isNameValid(namespace), + namespaceValidationError: !validator.isNamespaceValid(namespace), repository: { ...this.state.repository, namespace diff --git a/scm-ui/ui-webapp/src/repos/components/form/repositoryValidation.ts b/scm-ui/ui-webapp/src/repos/components/form/repositoryValidation.ts index b9ff7f6adb..c52a6b5e6f 100644 --- a/scm-ui/ui-webapp/src/repos/components/form/repositoryValidation.ts +++ b/scm-ui/ui-webapp/src/repos/components/form/repositoryValidation.ts @@ -25,6 +25,11 @@ import { validation } from "@scm-manager/ui-components"; const nameRegex = /(?!^\.\.$)(?!^\.$)(?!.*[.]git$)(?!.*[\\\[\]])^[A-Za-z0-9\.][A-Za-z0-9\.\-_]*$/; +const namespaceExceptionsRegex = /^(([0-9]{1,3})|(create))$/; + +export const isNamespaceValid = (name: string) => { + return nameRegex.test(name) && !namespaceExceptionsRegex.test(name); +}; export const isNameValid = (name: string) => { return nameRegex.test(name); diff --git a/scm-webapp/src/main/java/sonia/scm/repository/CustomNamespaceStrategy.java b/scm-webapp/src/main/java/sonia/scm/repository/CustomNamespaceStrategy.java index c38831ca17..bc28e7104f 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/CustomNamespaceStrategy.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/CustomNamespaceStrategy.java @@ -27,17 +27,25 @@ package sonia.scm.repository; import sonia.scm.plugin.Extension; import sonia.scm.util.ValidationUtil; +import java.util.regex.Pattern; + import static sonia.scm.ScmConstraintViolationException.Builder.doThrow; @Extension public class CustomNamespaceStrategy implements NamespaceStrategy { + + private static final Pattern ONE_TO_THREE_DIGITS = Pattern.compile("[0-9]{1,3}"); + @Override public String createNamespace(Repository repository) { String namespace = repository.getNamespace(); doThrow() .violation("invalid namespace", "namespace") - .when(!ValidationUtil.isRepositoryNameValid(namespace) || namespace.matches("[0-9]{1,3}")); + .when( + !ValidationUtil.isRepositoryNameValid(namespace) + || ONE_TO_THREE_DIGITS.matcher(namespace).matches() + || namespace.equals("create")); return namespace; } diff --git a/scm-webapp/src/test/java/sonia/scm/repository/CustomNamespaceStrategyTest.java b/scm-webapp/src/test/java/sonia/scm/repository/CustomNamespaceStrategyTest.java index 52d48ece75..ab17b4f7b2 100644 --- a/scm-webapp/src/test/java/sonia/scm/repository/CustomNamespaceStrategyTest.java +++ b/scm-webapp/src/test/java/sonia/scm/repository/CustomNamespaceStrategyTest.java @@ -21,10 +21,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.repository; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import sonia.scm.ScmConstraintViolationException; import static org.assertj.core.api.Assertions.assertThat; @@ -34,19 +35,21 @@ class CustomNamespaceStrategyTest { private final NamespaceStrategy namespaceStrategy = new CustomNamespaceStrategy(); - @Test - void shouldReturnNamespaceFromRepository() { - Repository heartOfGold = RepositoryTestData.createHeartOfGold(); - assertThat(namespaceStrategy.createNamespace(heartOfGold)).isEqualTo(RepositoryTestData.NAMESPACE); + @ParameterizedTest + @ValueSource(strings = {"valid", "123_", "something_valid", "1234", "create_it"}) + void shouldReturnNamespaceFromRepository(String expectedValidNamespace) { + Repository repository = RepositoryTestData.createHeartOfGold(); + repository.setNamespace(expectedValidNamespace); + + assertThat(namespaceStrategy.createNamespace(repository)).isEqualTo(expectedValidNamespace); } - @Test - void shouldThrowAnValidationExceptionForAnInvalidNamespace() { - Repository repository = new Repository(); - repository.setNamespace(".."); - repository.setName("."); + @ParameterizedTest + @ValueSource(strings = {"..", " ", "0", "123", "create"}) + void shouldThrowAnValidationExceptionForAnInvalidNamespace(String expectedAsInvalidNamespace) { + Repository repository = RepositoryTestData.createHeartOfGold(); + repository.setNamespace(expectedAsInvalidNamespace); assertThrows(ScmConstraintViolationException.class, () -> namespaceStrategy.createNamespace(repository)); } - }