Move all static ref pattern related methods into a RefPattern class
The goal of this refactoring is to have a single place where a ref
pattern is validated as regular expression. So far this was done in 2
places (in RefControl and in ProjectConfig). For validating a ref
pattern as regular expression we must remove placeholder variables
such as '${username}' because it would be an invalid regular
expression otherwise. We don't want to do this in several places
especially because we want to support further placeholder variables in
future.
Change-Id: I11c76e37f22233d89b631375ac6f2ef60f97801f
Signed-off-by: Edwin Kempin <ekempin@google.com>
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
index 980b284..4a01128 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
@@ -37,7 +37,7 @@
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
-import com.google.gerrit.server.project.RefControl;
+import com.google.gerrit.server.project.RefPattern;
import com.google.gerrit.server.project.SetParent;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Provider;
@@ -111,7 +111,7 @@
continue;
}
- RefControl.validateRefPattern(name);
+ RefPattern.validate(name);
replace(config, toDelete, section);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
index a0bc332..089afe8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
@@ -40,6 +40,7 @@
import com.google.gerrit.common.data.PermissionRule.Action;
import com.google.gerrit.common.data.RefConfigSection;
import com.google.gerrit.common.data.SubscribeSection;
+import com.google.gerrit.common.errors.InvalidNameException;
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.ProjectState;
import com.google.gerrit.extensions.client.SubmitType;
@@ -53,6 +54,7 @@
import com.google.gerrit.server.config.PluginConfig;
import com.google.gerrit.server.mail.Address;
import com.google.gerrit.server.project.CommentLinkInfo;
+import com.google.gerrit.server.project.RefPattern;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.CommitBuilder;
@@ -645,8 +647,8 @@
private boolean isValidRegex(String refPattern) {
try {
- Pattern.compile(refPattern.replace("${username}/", ""));
- } catch (PatternSyntaxException e) {
+ RefPattern.validateRegExp(refPattern);
+ } catch (InvalidNameException e) {
error(new ValidationError(PROJECT_CONFIG, "Invalid ref name: "
+ e.getMessage()));
return false;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java
index ee6fac7..a862ac2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.project;
import static com.google.common.base.MoreObjects.firstNonNull;
-import static com.google.gerrit.server.project.RefControl.isRE;
+import static com.google.gerrit.server.project.RefPattern.isRE;
import com.google.auto.value.AutoValue;
import com.google.common.collect.Lists;
@@ -74,7 +74,7 @@
PermissionCollection filter(Iterable<SectionMatcher> matcherList,
String ref, CurrentUser user) {
if (isRE(ref)) {
- ref = RefControl.shortestExample(ref);
+ ref = RefPattern.shortestExample(ref);
} else if (ref.endsWith("/*")) {
ref = ref.substring(0, ref.length() - 1);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
index cbe7fba..0e60f3f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
@@ -14,20 +14,15 @@
package com.google.gerrit.server.project;
-import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.common.data.PermissionRule;
-import com.google.gerrit.common.data.RefConfigSection;
-import com.google.gerrit.common.errors.InvalidNameException;
import com.google.gerrit.extensions.client.ProjectState;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.group.SystemGroupBackend;
-import dk.brics.automaton.RegExp;
-
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
@@ -47,8 +42,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
/** Manages access control for Git references (aka branches, tags). */
@@ -646,53 +639,4 @@
effective.put(permissionName, mine);
return mine;
}
-
- public static boolean isRE(String refPattern) {
- return refPattern.startsWith(AccessSection.REGEX_PREFIX);
- }
-
- public static String shortestExample(String pattern) {
- if (isRE(pattern)) {
- // Since Brics will substitute dot [.] with \0 when generating
- // shortest example, any usage of dot will fail in
- // Repository.isValidRefName() if not combined with star [*].
- // To get around this, we substitute the \0 with an arbitrary
- // accepted character.
- return toRegExp(pattern).toAutomaton().getShortestExample(true).replace('\0', '-');
- } else if (pattern.endsWith("/*")) {
- return pattern.substring(0, pattern.length() - 1) + '1';
- } else {
- return pattern;
- }
- }
-
- public static RegExp toRegExp(String refPattern) {
- if (isRE(refPattern)) {
- refPattern = refPattern.substring(1);
- }
- return new RegExp(refPattern, RegExp.NONE);
- }
-
- public static void validateRefPattern(String refPattern)
- throws InvalidNameException {
- if (refPattern.startsWith(RefConfigSection.REGEX_PREFIX)) {
- if (!Repository.isValidRefName(RefControl.shortestExample(refPattern))) {
- throw new InvalidNameException(refPattern);
- }
- } else if (refPattern.equals(RefConfigSection.ALL)) {
- // This is a special case we have to allow, it fails below.
- } else if (refPattern.endsWith("/*")) {
- String prefix = refPattern.substring(0, refPattern.length() - 2);
- if (!Repository.isValidRefName(prefix)) {
- throw new InvalidNameException(refPattern);
- }
- } else if (!Repository.isValidRefName(refPattern)) {
- throw new InvalidNameException(refPattern);
- }
- try {
- Pattern.compile(refPattern.replace("${username}/", ""));
- } catch (PatternSyntaxException e) {
- throw new InvalidNameException(refPattern + " " + e.getMessage());
- }
- }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefPattern.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefPattern.java
new file mode 100644
index 0000000..2b03951
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefPattern.java
@@ -0,0 +1,85 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.project;
+
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.RefConfigSection;
+import com.google.gerrit.common.errors.InvalidNameException;
+
+import dk.brics.automaton.RegExp;
+
+import org.eclipse.jgit.lib.Repository;
+
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+public class RefPattern {
+ public static final String USERNAME = "username";
+
+ public static String shortestExample(String refPattern) {
+ if (isRE(refPattern)) {
+ // Since Brics will substitute dot [.] with \0 when generating
+ // shortest example, any usage of dot will fail in
+ // Repository.isValidRefName() if not combined with star [*].
+ // To get around this, we substitute the \0 with an arbitrary
+ // accepted character.
+ return toRegExp(refPattern).toAutomaton().getShortestExample(true)
+ .replace('\0', '-');
+ } else if (refPattern.endsWith("/*")) {
+ return refPattern.substring(0, refPattern.length() - 1) + '1';
+ } else {
+ return refPattern;
+ }
+ }
+
+ public static boolean isRE(String refPattern) {
+ return refPattern.startsWith(AccessSection.REGEX_PREFIX);
+ }
+
+ public static RegExp toRegExp(String refPattern) {
+ if (isRE(refPattern)) {
+ refPattern = refPattern.substring(1);
+ }
+ return new RegExp(refPattern, RegExp.NONE);
+ }
+
+ public static void validate(String refPattern)
+ throws InvalidNameException {
+ if (refPattern.startsWith(RefConfigSection.REGEX_PREFIX)) {
+ if (!Repository.isValidRefName(shortestExample(refPattern))) {
+ throw new InvalidNameException(refPattern);
+ }
+ } else if (refPattern.equals(RefConfigSection.ALL)) {
+ // This is a special case we have to allow, it fails below.
+ } else if (refPattern.endsWith("/*")) {
+ String prefix = refPattern.substring(0, refPattern.length() - 2);
+ if (!Repository.isValidRefName(prefix)) {
+ throw new InvalidNameException(refPattern);
+ }
+ } else if (!Repository.isValidRefName(refPattern)) {
+ throw new InvalidNameException(refPattern);
+ }
+ validateRegExp(refPattern);
+ }
+
+ public static void validateRegExp(String refPattern)
+ throws InvalidNameException {
+ try {
+ Pattern.compile(refPattern.replace("${" + USERNAME + "}/", ""));
+ } catch (PatternSyntaxException e) {
+ throw new InvalidNameException(refPattern + " " + e.getMessage());
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefPatternMatcher.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefPatternMatcher.java
index 96b9e10..97f70b4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefPatternMatcher.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefPatternMatcher.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.project;
-import static com.google.gerrit.server.project.RefControl.isRE;
+import static com.google.gerrit.server.project.RefPattern.isRE;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@@ -93,12 +93,13 @@
// in a reference and the string :USERNAME: is not likely to
// be a valid part of the regex. This later allows the pattern
// prefix to be clipped, saving time on evaluation.
+ String replacement = ":" + RefPattern.USERNAME.toUpperCase() + ":";
Automaton am =
- RefControl.toRegExp(
- template.replace(Collections.singletonMap("username",
- ":USERNAME:"))).toAutomaton();
+ RefPattern.toRegExp(
+ template.replace(Collections.singletonMap(RefPattern.USERNAME,
+ replacement))).toAutomaton();
String rePrefix = am.getCommonPrefix();
- prefix = rePrefix.substring(0, rePrefix.indexOf(":USERNAME:"));
+ prefix = rePrefix.substring(0, rePrefix.indexOf(replacement));
} else {
prefix = pattern.substring(0, pattern.indexOf("${"));
}
@@ -154,8 +155,8 @@
}
private String expand(ParameterizedString parameterizedRef, String userName) {
- return parameterizedRef.replace(Collections.singletonMap("username",
- userName));
+ return parameterizedRef
+ .replace(Collections.singletonMap(RefPattern.USERNAME, userName));
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java
index 258b386..c0d23b9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java
@@ -146,7 +146,7 @@
throw new AuthException("You are not allowed to edit permissions"
+ "for ref: " + name);
}
- RefControl.validateRefPattern(name);
+ RefPattern.validate(name);
}
// Check all permissions for soundness
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/MostSpecificComparator.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/MostSpecificComparator.java
index ce7f24d..159763c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/util/MostSpecificComparator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/MostSpecificComparator.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.util;
import com.google.gerrit.common.data.RefConfigSection;
-import com.google.gerrit.server.project.RefControl;
+import com.google.gerrit.server.project.RefPattern;
import org.apache.commons.lang.StringUtils;
@@ -82,8 +82,8 @@
private int distance(String pattern) {
String example;
- if (RefControl.isRE(pattern)) {
- example = RefControl.shortestExample(pattern);
+ if (RefPattern.isRE(pattern)) {
+ example = RefPattern.shortestExample(pattern);
} else if (pattern.endsWith("/*")) {
example = pattern;
@@ -98,8 +98,8 @@
}
private boolean finite(String pattern) {
- if (RefControl.isRE(pattern)) {
- return RefControl.toRegExp(pattern).toAutomaton().isFinite();
+ if (RefPattern.isRE(pattern)) {
+ return RefPattern.toRegExp(pattern).toAutomaton().isFinite();
} else if (pattern.endsWith("/*")) {
return false;
@@ -110,8 +110,8 @@
}
private int transitions(String pattern) {
- if (RefControl.isRE(pattern)) {
- return RefControl.toRegExp(pattern).toAutomaton()
+ if (RefPattern.isRE(pattern)) {
+ return RefPattern.toRegExp(pattern).toAutomaton()
.getNumberOfTransitions();
} else if (pattern.endsWith("/*")) {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
index 1e65560..073cfec 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
@@ -823,27 +823,26 @@
@Test
public void testValidateRefPatternsOK() throws Exception {
- RefControl.validateRefPattern("refs/*");
- RefControl.validateRefPattern("^refs/heads/*");
- RefControl.validateRefPattern("^refs/tags/[0-9a-zA-Z-_.]+");
- RefControl.validateRefPattern("refs/heads/review/${username}/*");
+ RefPattern.validate("refs/*");
+ RefPattern.validate("^refs/heads/*");
+ RefPattern.validate("^refs/tags/[0-9a-zA-Z-_.]+");
+ RefPattern.validate("refs/heads/review/${username}/*");
}
@Test(expected = InvalidNameException.class)
public void testValidateBadRefPatternDoubleCaret() throws Exception {
- RefControl.validateRefPattern("^^refs/*");
+ RefPattern.validate("^^refs/*");
}
@Test(expected = InvalidNameException.class)
public void testValidateBadRefPatternDanglingCharacter() throws Exception {
- RefControl
- .validateRefPattern("^refs/heads/tmp/sdk/[0-9]{3,3}_R[1-9][A-Z][0-9]{3,3}*");
+ RefPattern
+ .validate("^refs/heads/tmp/sdk/[0-9]{3,3}_R[1-9][A-Z][0-9]{3,3}*");
}
@Test
public void testValidateRefPatternNoDanglingCharacter() throws Exception {
- RefControl
- .validateRefPattern("^refs/heads/tmp/sdk/[0-9]{3,3}_R[1-9][A-Z][0-9]{3,3}");
+ RefPattern.validate("^refs/heads/tmp/sdk/[0-9]{3,3}_R[1-9][A-Z][0-9]{3,3}");
}
private InMemoryRepository add(ProjectConfig pc) {