align validation of repository name and namespace

This commit is contained in:
Sebastian Sdorra
2019-03-12 15:10:05 +01:00
parent c180457214
commit f7f5102541
5 changed files with 93 additions and 14 deletions

View File

@@ -248,7 +248,8 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per
/** /**
* Returns true if the {@link Repository} is valid. * Returns true if the {@link Repository} is valid.
* <ul> * <ul>
* <li>The name is not empty and contains only A-z, 0-9, _, -, /</li> * <li>The namespace is valid</li>
* <li>The name is valid</li>
* <li>The type is not empty</li> * <li>The type is not empty</li>
* <li>The contact is empty or contains a valid email address</li> * <li>The contact is empty or contains a valid email address</li>
* </ul> * </ul>
@@ -257,9 +258,10 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per
*/ */
@Override @Override
public boolean isValid() { public boolean isValid() {
return ValidationUtil.isRepositoryNameValid(name) && Util.isNotEmpty(type) return ValidationUtil.isRepositoryNameValid(namespace)
&& ((Util.isEmpty(contact)) && ValidationUtil.isRepositoryNameValid(name)
|| ValidationUtil.isMailAddressValid(contact)); && Util.isNotEmpty(type)
&& ((Util.isEmpty(contact)) || ValidationUtil.isMailAddressValid(contact));
} }
/** /**

View File

@@ -35,14 +35,12 @@ package sonia.scm.util;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Splitter;
import sonia.scm.Validateable; import sonia.scm.Validateable;
//~--- JDK imports ------------------------------------------------------------
import java.util.regex.Pattern; import java.util.regex.Pattern;
//~--- JDK imports ------------------------------------------------------------
/** /**
* *
* @author Sebastian Sdorra * @author Sebastian Sdorra
@@ -58,10 +56,10 @@ public final class ValidationUtil
private static final String REGEX_NAME = private static final String REGEX_NAME =
"^[A-z0-9\\.\\-_@]|[^ ]([A-z0-9\\.\\-_@ ]*[A-z0-9\\.\\-_@]|[^ ])?$"; "^[A-z0-9\\.\\-_@]|[^ ]([A-z0-9\\.\\-_@ ]*[A-z0-9\\.\\-_@]|[^ ])?$";
public static final String REGEX_REPOSITORYNAME = "(?!^\\.\\.$)(?!^\\.$)(?!.*[\\\\\\[\\]])^[A-z0-9\\.][A-z0-9\\.\\-_]*$";
/** Field description */ /** Field description */
private static final Pattern REGEX_REPOSITORYNAME = Pattern.compile( private static final Pattern PATTERN_REPOSITORYNAME = Pattern.compile(REGEX_REPOSITORYNAME);
"(?!^\\.\\.$)(?!^\\.$)(?!.*[\\\\\\[\\]])^[A-z0-9\\.][A-z0-9\\.\\-_]*$"
);
//~--- constructors --------------------------------------------------------- //~--- constructors ---------------------------------------------------------
@@ -151,7 +149,7 @@ public final class ValidationUtil
* @return {@code true} if repository name is valid * @return {@code true} if repository name is valid
*/ */
public static boolean isRepositoryNameValid(String name) { public static boolean isRepositoryNameValid(String name) {
return REGEX_REPOSITORYNAME.matcher(name).matches(); return PATTERN_REPOSITORYNAME.matcher(name).matches();
} }
/** /**

View File

@@ -1,8 +1,10 @@
// @flow // @flow
import { validation } from "@scm-manager/ui-components"; import { validation } from "@scm-manager/ui-components";
const nameRegex = /(?!^\.\.$)(?!^\.$)(?!.*[\\\[\]])^[A-z0-9\.][A-z0-9\.\-_]*$/;
export const isNameValid = (name: string) => { export const isNameValid = (name: string) => {
return validation.isNameValid(name); return nameRegex.test(name);
}; };
export function isContactValid(mail: string) { export function isContactValid(mail: string) {

View File

@@ -11,6 +11,81 @@ describe("repository name validation", () => {
expect(validator.isNameValid("scm/manager")).toBe(false); expect(validator.isNameValid("scm/manager")).toBe(false);
expect(validator.isNameValid("scm/ma/nager")).toBe(false); expect(validator.isNameValid("scm/ma/nager")).toBe(false);
}); });
it("should allow same names as the backend", () => {
const validPaths = [
"scm",
"s",
"sc",
".hiddenrepo",
"b.",
"...",
"..c",
"d..",
"a..c"
];
validPaths.forEach((path) =>
expect(validator.isNameValid(path)).toBe(true)
);
});
it("should deny same names as the backend", () => {
const invalidPaths = [
".",
"/",
"//",
"..",
"/.",
"/..",
"./",
"../",
"/../",
"/./",
"/...",
"/abc",
".../",
"/sdf/",
"asdf/",
"./b",
"scm/plugins/.",
"scm/../plugins",
"scm/main/",
"/scm/main/",
"scm/./main",
"scm//main",
"scm\\main",
"scm/main-$HOME",
"scm/main-${HOME}-home",
"scm/main-%HOME-home",
"scm/main-%HOME%-home",
"abc$abc",
"abc%abc",
"abc<abc",
"abc>abc",
"abc#abc",
"abc+abc",
"abc{abc",
"abc}abc",
"abc(abc",
"abc)abc",
"abc[abc",
"abc]abc",
"abc|abc",
"scm/main",
"scm/plugins/git-plugin",
".scm/plugins",
"a/b..",
"a/..b",
"scm/main",
"scm/plugins/git-plugin",
"scm/plugins/git-plugin"
];
invalidPaths.forEach((path) =>
expect(validator.isNameValid(path)).toBe(false)
);
});
}); });
describe("repository contact validation", () => { describe("repository contact validation", () => {

View File

@@ -9,6 +9,7 @@ import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.NotEmpty;
import sonia.scm.util.ValidationUtil;
import javax.validation.constraints.Pattern; import javax.validation.constraints.Pattern;
import java.time.Instant; import java.time.Instant;
@@ -25,8 +26,9 @@ public class RepositoryDto extends HalRepresentation {
private List<HealthCheckFailureDto> healthCheckFailures; private List<HealthCheckFailureDto> healthCheckFailures;
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
private Instant lastModified; private Instant lastModified;
// we could not validate the namespace, this must be done by the namespace strategy
private String namespace; private String namespace;
@Pattern(regexp = "^[A-z0-9\\-_]+$") @Pattern(regexp = ValidationUtil.REGEX_REPOSITORYNAME)
private String name; private String name;
private boolean archived = false; private boolean archived = false;
@NotEmpty @NotEmpty