Support regular expressions for ref access rules
This change considers the rights of the most specific ref pattern. If the ref pattern starts with ^ it is considered to be a regular expression, otherwise the older glob style or exact match rules are used. Change-Id: Ie060d3758e5184a7cedd38883253f60817a04e1b Portions-by: carloseduardo.baldacin <carloseduardo.baldacin@sonyericsson.com> Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
@@ -22,6 +22,7 @@ Guice <<apache2,Apache License 2.0>>
|
|||||||
Apache Commons Codec <<apache2,Apache License 2.0>>
|
Apache Commons Codec <<apache2,Apache License 2.0>>
|
||||||
Apache Commons DBCP <<apache2,Apache License 2.0>>
|
Apache Commons DBCP <<apache2,Apache License 2.0>>
|
||||||
Apache Commons Http Client <<apache2,Apache License 2.0>>
|
Apache Commons Http Client <<apache2,Apache License 2.0>>
|
||||||
|
Apache Commons Lang <<apache2,Apache License 2.0>>
|
||||||
Apache Commons Logging <<apache2,Apache License 2.0>>
|
Apache Commons Logging <<apache2,Apache License 2.0>>
|
||||||
Apache Commons Net <<apache2,Apache License 2.0>>
|
Apache Commons Net <<apache2,Apache License 2.0>>
|
||||||
Apache Commons Pool <<apache2,Apache License 2.0>>
|
Apache Commons Pool <<apache2,Apache License 2.0>>
|
||||||
@@ -48,6 +49,7 @@ Clippy <<clippy,MIT License>>
|
|||||||
juniversalchardet <<mpl1_1,MPL 1.1>>
|
juniversalchardet <<mpl1_1,MPL 1.1>>
|
||||||
AOP Alliance Public Domain
|
AOP Alliance Public Domain
|
||||||
JSR 305 <<jsr305,New-Style BSD>>
|
JSR 305 <<jsr305,New-Style BSD>>
|
||||||
|
Automaton <<automaton,New-Style BSD>>
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
|
|
||||||
Cryptography Notice
|
Cryptography Notice
|
||||||
@@ -560,6 +562,43 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|||||||
POSSIBILITY OF SUCH DAMAGE.
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
----
|
----
|
||||||
|
|
||||||
|
[[automaton]]
|
||||||
|
dk.brics.automaton - The BSD License
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* link:http://www.brics.dk/automaton/index.html
|
||||||
|
|
||||||
|
----
|
||||||
|
Copyright (c) 2007-2009, dk.brics.automaton
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
http://www.opensource.org/licenses/bsd-license.php
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the JSR305 expert group nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
----
|
||||||
|
|
||||||
[[args4j]]
|
[[args4j]]
|
||||||
args4j - MIT License
|
args4j - MIT License
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|||||||
@@ -144,9 +144,25 @@ class AddRefRight extends Handler<ProjectDetail> {
|
|||||||
while (refPattern.startsWith("/")) {
|
while (refPattern.startsWith("/")) {
|
||||||
refPattern = refPattern.substring(1);
|
refPattern = refPattern.substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (refPattern.startsWith(RefRight.REGEX_PREFIX)) {
|
||||||
|
String example = RefControl.shortestExample(refPattern);
|
||||||
|
|
||||||
|
if (!example.startsWith(Constants.R_REFS)) {
|
||||||
|
refPattern = RefRight.REGEX_PREFIX + Constants.R_HEADS
|
||||||
|
+ refPattern.substring(RefRight.REGEX_PREFIX.length());
|
||||||
|
example = RefControl.shortestExample(refPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Repository.isValidRefName(example)) {
|
||||||
|
throw new InvalidNameException();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
if (!refPattern.startsWith(Constants.R_REFS)) {
|
if (!refPattern.startsWith(Constants.R_REFS)) {
|
||||||
refPattern = Constants.R_HEADS + refPattern;
|
refPattern = Constants.R_HEADS + refPattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refPattern.endsWith("/*")) {
|
if (refPattern.endsWith("/*")) {
|
||||||
final String prefix = refPattern.substring(0, refPattern.length() - 2);
|
final String prefix = refPattern.substring(0, refPattern.length() - 2);
|
||||||
if (!"refs".equals(prefix) && !Repository.isValidRefName(prefix)) {
|
if (!"refs".equals(prefix) && !Repository.isValidRefName(prefix)) {
|
||||||
@@ -157,12 +173,13 @@ class AddRefRight extends Handler<ProjectDetail> {
|
|||||||
throw new InvalidNameException();
|
throw new InvalidNameException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (exclusive) {
|
if (exclusive) {
|
||||||
refPattern = "-" + refPattern;
|
refPattern = "-" + refPattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!controlForRef(projectControl, refPattern).isOwner()) {
|
if (!projectControl.controlForRef(refPattern).isOwner()) {
|
||||||
throw new NoSuchRefException(refPattern);
|
throw new NoSuchRefException(refPattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,11 +204,4 @@ class AddRefRight extends Handler<ProjectDetail> {
|
|||||||
projectCache.evictAll();
|
projectCache.evictAll();
|
||||||
return projectDetailFactory.create(projectName).call();
|
return projectDetailFactory.create(projectName).call();
|
||||||
}
|
}
|
||||||
|
|
||||||
private RefControl controlForRef(ProjectControl p, String ref) {
|
|
||||||
if (ref.endsWith("/*")) {
|
|
||||||
ref = ref.substring(0, ref.length() - 1);
|
|
||||||
}
|
|
||||||
return p.controlForRef(ref);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class DeleteRefRights extends Handler<ProjectDetail> {
|
|||||||
if (!projectName.equals(k.getProjectNameKey())) {
|
if (!projectName.equals(k.getProjectNameKey())) {
|
||||||
throw new IllegalArgumentException("All keys must be from same project");
|
throw new IllegalArgumentException("All keys must be from same project");
|
||||||
}
|
}
|
||||||
if (!controlForRef(projectControl, k.getRefPattern()).isOwner()) {
|
if (!projectControl.controlForRef(k.getRefPattern()).isOwner()) {
|
||||||
throw new NoSuchRefException(k.getRefPattern());
|
throw new NoSuchRefException(k.getRefPattern());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,11 +85,4 @@ class DeleteRefRights extends Handler<ProjectDetail> {
|
|||||||
projectCache.evictAll();
|
projectCache.evictAll();
|
||||||
return projectDetailFactory.create(projectName).call();
|
return projectDetailFactory.create(projectName).call();
|
||||||
}
|
}
|
||||||
|
|
||||||
private RefControl controlForRef(ProjectControl p, String ref) {
|
|
||||||
if (ref.endsWith("/*")) {
|
|
||||||
ref = ref.substring(0, ref.length() - 1);
|
|
||||||
}
|
|
||||||
return p.controlForRef(ref);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import com.google.gerrit.server.account.GroupCache;
|
|||||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
import com.google.gerrit.server.project.NoSuchProjectException;
|
||||||
import com.google.gerrit.server.project.ProjectControl;
|
import com.google.gerrit.server.project.ProjectControl;
|
||||||
import com.google.gerrit.server.project.ProjectState;
|
import com.google.gerrit.server.project.ProjectState;
|
||||||
import com.google.gerrit.server.project.RefControl;
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
|
||||||
@@ -77,7 +76,7 @@ class ProjectDetailFactory extends Handler<ProjectDetail> {
|
|||||||
|
|
||||||
for (final RefRight r : projectState.getInheritedRights()) {
|
for (final RefRight r : projectState.getInheritedRights()) {
|
||||||
InheritedRefRight refRight = new InheritedRefRight(
|
InheritedRefRight refRight = new InheritedRefRight(
|
||||||
r, true, controlForRef(pc, r.getRefPattern()).isOwner());
|
r, true, pc.controlForRef(r.getRefPattern()).isOwner());
|
||||||
if (!refRights.contains(refRight)) {
|
if (!refRights.contains(refRight)) {
|
||||||
refRights.add(refRight);
|
refRights.add(refRight);
|
||||||
wantGroup(r.getAccountGroupId());
|
wantGroup(r.getAccountGroupId());
|
||||||
@@ -86,7 +85,7 @@ class ProjectDetailFactory extends Handler<ProjectDetail> {
|
|||||||
|
|
||||||
for (final RefRight r : projectState.getLocalRights()) {
|
for (final RefRight r : projectState.getLocalRights()) {
|
||||||
refRights.add(new InheritedRefRight(
|
refRights.add(new InheritedRefRight(
|
||||||
r, false, controlForRef(pc, r.getRefPattern()).isOwner()));
|
r, false, pc.controlForRef(r.getRefPattern()).isOwner()));
|
||||||
wantGroup(r.getAccountGroupId());
|
wantGroup(r.getAccountGroupId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,11 +143,4 @@ class ProjectDetailFactory extends Handler<ProjectDetail> {
|
|||||||
groups.put(groupId, groupCache.get(groupId));
|
groups.put(groupId, groupCache.get(groupId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private RefControl controlForRef(ProjectControl p, String ref) {
|
|
||||||
if (ref.endsWith("/*")) {
|
|
||||||
ref = ref.substring(0, ref.length() - 1);
|
|
||||||
}
|
|
||||||
return p.controlForRef(ref);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ public final class RefRight {
|
|||||||
/** Pattern that matches all references in a project. */
|
/** Pattern that matches all references in a project. */
|
||||||
public static final String ALL = "refs/*";
|
public static final String ALL = "refs/*";
|
||||||
|
|
||||||
|
/** Prefix that triggers a regular expression pattern. */
|
||||||
|
public static final String REGEX_PREFIX = "^";
|
||||||
|
|
||||||
public static class RefPattern extends
|
public static class RefPattern extends
|
||||||
StringKey<com.google.gwtorm.client.Key<?>> {
|
StringKey<com.google.gwtorm.client.Key<?>> {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|||||||
@@ -53,6 +53,11 @@ limitations under the License.
|
|||||||
<artifactId>commons-dbcp</artifactId>
|
<artifactId>commons-dbcp</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-lang</groupId>
|
||||||
|
<artifactId>commons-lang</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-net</groupId>
|
<groupId>commons-net</groupId>
|
||||||
<artifactId>commons-net</artifactId>
|
<artifactId>commons-net</artifactId>
|
||||||
@@ -133,6 +138,11 @@ limitations under the License.
|
|||||||
<groupId>com.google.gerrit</groupId>
|
<groupId>com.google.gerrit</groupId>
|
||||||
<artifactId>juniversalchardet</artifactId>
|
<artifactId>juniversalchardet</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>dk.brics.automaton</groupId>
|
||||||
|
<artifactId>automaton</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ import com.google.gerrit.reviewdb.RefRight;
|
|||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
|
|
||||||
|
import dk.brics.automaton.RegExp;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.eclipse.jgit.lib.PersonIdent;
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
import org.eclipse.jgit.revwalk.RevObject;
|
import org.eclipse.jgit.revwalk.RevObject;
|
||||||
@@ -49,6 +52,7 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.SortedMap;
|
import java.util.SortedMap;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
|
||||||
/** Manages access control for Git references (aka branches, tags). */
|
/** Manages access control for Git references (aka branches, tags). */
|
||||||
@@ -59,9 +63,17 @@ public class RefControl {
|
|||||||
private Boolean canForgeAuthor;
|
private Boolean canForgeAuthor;
|
||||||
private Boolean canForgeCommitter;
|
private Boolean canForgeCommitter;
|
||||||
|
|
||||||
RefControl(final ProjectControl projectControl, final String refName) {
|
RefControl(final ProjectControl projectControl, String ref) {
|
||||||
|
if (ref.startsWith(RefRight.REGEX_PREFIX)) {
|
||||||
|
ref = shortestExample(ref);
|
||||||
|
|
||||||
|
} else if (ref.endsWith("/*")) {
|
||||||
|
ref = ref.substring(0, ref.length() - 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
this.projectControl = projectControl;
|
this.projectControl = projectControl;
|
||||||
this.refName = refName;
|
this.refName = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRefName() {
|
public String getRefName() {
|
||||||
@@ -94,7 +106,9 @@ public class RefControl {
|
|||||||
// calls us to find out if there is ownership of all references in
|
// calls us to find out if there is ownership of all references in
|
||||||
// order to determine project level ownership.
|
// order to determine project level ownership.
|
||||||
//
|
//
|
||||||
if (!RefRight.ALL.equals(getRefName()) && getProjectControl().isOwner()) {
|
if (getRefName().equals(
|
||||||
|
RefRight.ALL.substring(0, RefRight.ALL.length() - 1))
|
||||||
|
&& getProjectControl().isOwner()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,17 +316,93 @@ public class RefControl {
|
|||||||
return val >= level;
|
return val >= level;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Comparator<String> DESCENDING_SORT =
|
/**
|
||||||
|
* Order the Ref Pattern by the most specific. This sort is done by:
|
||||||
|
* <ul>
|
||||||
|
* <li>1 - The minor value of Levenshtein string distance between the branch
|
||||||
|
* name and the regex string shortest example. A shorter distance is a more
|
||||||
|
* specific match.
|
||||||
|
* <li>2 - Finites first, infinities after.
|
||||||
|
* <li>3 - Number of transitions.
|
||||||
|
* <li>4 - Length of the expression text.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* Levenshtein distance is a measure of the similarity between two strings.
|
||||||
|
* The distance is the number of deletions, insertions, or substitutions
|
||||||
|
* required to transform one string into another.
|
||||||
|
*
|
||||||
|
* For example, if given refs/heads/m* and refs/heads/*, the distances are 5
|
||||||
|
* and 6. It means that refs/heads/m* is more specific because it's closer to
|
||||||
|
* refs/heads/master than refs/heads/*.
|
||||||
|
*
|
||||||
|
* Another example could be refs/heads/* and refs/heads/[a-zA-Z]*, the
|
||||||
|
* distances are both 6. Both are infinite, but refs/heads/[a-zA-Z]* has more
|
||||||
|
* transitions, which after all turns it more specific.
|
||||||
|
*/
|
||||||
|
private final Comparator<String> BY_MOST_SPECIFIC_SORT =
|
||||||
new Comparator<String>() {
|
new Comparator<String>() {
|
||||||
|
public int compare(final String pattern1, final String pattern2) {
|
||||||
|
int cmp = distance(pattern2) - distance(pattern1);
|
||||||
|
if (cmp == 0) {
|
||||||
|
boolean p1_finite = finite(pattern1);
|
||||||
|
boolean p2_finite = finite(pattern2);
|
||||||
|
|
||||||
@Override
|
if (p1_finite && !p2_finite) {
|
||||||
public int compare(String a, String b) {
|
cmp = -1;
|
||||||
int aLength = a.length();
|
} else if (!p1_finite && p2_finite) {
|
||||||
int bLength = b.length();
|
cmp = 1;
|
||||||
if (bLength == aLength) {
|
} else /* if (f1 == f2) */{
|
||||||
return a.compareTo(b);
|
cmp = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cmp == 0) {
|
||||||
|
cmp = transitions(pattern1) - transitions(pattern2);
|
||||||
|
}
|
||||||
|
if (cmp == 0) {
|
||||||
|
cmp = pattern2.length() - pattern1.length();
|
||||||
|
}
|
||||||
|
return cmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int distance(String pattern) {
|
||||||
|
String example;
|
||||||
|
if (pattern.startsWith(RefRight.REGEX_PREFIX)) {
|
||||||
|
example = shortestExample(pattern);
|
||||||
|
|
||||||
|
} else if (pattern.endsWith("/*")) {
|
||||||
|
example = pattern.substring(0, pattern.length() - 1) + '1';
|
||||||
|
|
||||||
|
} else if (pattern.equals(getRefName())) {
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return Math.max(pattern.length(), getRefName().length());
|
||||||
|
}
|
||||||
|
return StringUtils.getLevenshteinDistance(example, getRefName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean finite(String pattern) {
|
||||||
|
if (pattern.startsWith(RefRight.REGEX_PREFIX)) {
|
||||||
|
return toRegExp(pattern).toAutomaton().isFinite();
|
||||||
|
|
||||||
|
} else if (pattern.endsWith("/*")) {
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int transitions(String pattern) {
|
||||||
|
if (pattern.startsWith(RefRight.REGEX_PREFIX)) {
|
||||||
|
return toRegExp(pattern).toAutomaton().getNumberOfTransitions();
|
||||||
|
|
||||||
|
} else if (pattern.endsWith("/*")) {
|
||||||
|
return pattern.length();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return pattern.length();
|
||||||
}
|
}
|
||||||
return bLength - aLength;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -338,10 +428,10 @@ public class RefControl {
|
|||||||
* @param actionRights
|
* @param actionRights
|
||||||
* @return A sorted map keyed off the ref pattern of all rights.
|
* @return A sorted map keyed off the ref pattern of all rights.
|
||||||
*/
|
*/
|
||||||
private static SortedMap<String, RefRightsForPattern> sortedRightsByPattern(
|
private SortedMap<String, RefRightsForPattern> sortedRightsByPattern(
|
||||||
List<RefRight> actionRights) {
|
List<RefRight> actionRights) {
|
||||||
SortedMap<String, RefRightsForPattern> rights =
|
SortedMap<String, RefRightsForPattern> rights =
|
||||||
new TreeMap<String, RefRightsForPattern>(DESCENDING_SORT);
|
new TreeMap<String, RefRightsForPattern>(BY_MOST_SPECIFIC_SORT);
|
||||||
for (RefRight actionRight : actionRights) {
|
for (RefRight actionRight : actionRights) {
|
||||||
RefRightsForPattern patternRights =
|
RefRightsForPattern patternRights =
|
||||||
rights.get(actionRight.getRefPattern());
|
rights.get(actionRight.getRefPattern());
|
||||||
@@ -403,6 +493,10 @@ public class RefControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean matches(String refName, String refPattern) {
|
public static boolean matches(String refName, String refPattern) {
|
||||||
|
if (refPattern.startsWith(RefRight.REGEX_PREFIX)) {
|
||||||
|
return Pattern.matches(refPattern, refName);
|
||||||
|
}
|
||||||
|
|
||||||
if (refPattern.endsWith("/*")) {
|
if (refPattern.endsWith("/*")) {
|
||||||
String prefix = refPattern.substring(0, refPattern.length() - 1);
|
String prefix = refPattern.substring(0, refPattern.length() - 1);
|
||||||
return refName.startsWith(prefix);
|
return refName.startsWith(prefix);
|
||||||
@@ -411,4 +505,21 @@ public class RefControl {
|
|||||||
return refName.equals(refPattern);
|
return refName.equals(refPattern);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String shortestExample(String pattern) {
|
||||||
|
if (pattern.startsWith(RefRight.REGEX_PREFIX)) {
|
||||||
|
return toRegExp(pattern).toAutomaton().getShortestExample(true);
|
||||||
|
} else if (pattern.endsWith("/*")) {
|
||||||
|
return pattern.substring(0, pattern.length() - 1) + '1';
|
||||||
|
} else {
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RegExp toRegExp(String refPattern) {
|
||||||
|
if (refPattern.startsWith(RefRight.REGEX_PREFIX)) {
|
||||||
|
refPattern = refPattern.substring(1);
|
||||||
|
}
|
||||||
|
return new RegExp(refPattern);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,19 +19,33 @@ import com.google.gerrit.reviewdb.Project;
|
|||||||
import com.google.gerrit.reviewdb.RefRight;
|
import com.google.gerrit.reviewdb.RefRight;
|
||||||
import com.google.gerrit.reviewdb.ReviewDb;
|
import com.google.gerrit.reviewdb.ReviewDb;
|
||||||
import com.google.gerrit.reviewdb.RefRight.RefPattern;
|
import com.google.gerrit.reviewdb.RefRight.RefPattern;
|
||||||
import com.google.gerrit.server.project.RefControl;
|
|
||||||
import com.google.gerrit.server.project.RefControl.RefRightsForPattern;
|
import com.google.gerrit.server.project.RefControl.RefRightsForPattern;
|
||||||
import com.google.gwtorm.client.OrmException;
|
import com.google.gwtorm.client.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
public class Schema_34 extends SchemaVersion {
|
public class Schema_34 extends SchemaVersion {
|
||||||
|
private static final Comparator<String> DESCENDING_SORT =
|
||||||
|
new Comparator<String>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(String a, String b) {
|
||||||
|
int aLength = a.length();
|
||||||
|
int bLength = b.length();
|
||||||
|
if (bLength == aLength) {
|
||||||
|
return a.compareTo(b);
|
||||||
|
}
|
||||||
|
return bLength - aLength;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Schema_34(Provider<Schema_33> prior) {
|
Schema_34(Provider<Schema_33> prior) {
|
||||||
super(prior);
|
super(prior);
|
||||||
@@ -54,7 +68,7 @@ public class Schema_34 extends SchemaVersion {
|
|||||||
ApprovalCategory.Id cat = right.getApprovalCategoryId();
|
ApprovalCategory.Id cat = right.getApprovalCategoryId();
|
||||||
if (r.get(cat) == null) {
|
if (r.get(cat) == null) {
|
||||||
Map<String, RefRightsForPattern> m =
|
Map<String, RefRightsForPattern> m =
|
||||||
new TreeMap<String, RefRightsForPattern>(RefControl.DESCENDING_SORT);
|
new TreeMap<String, RefRightsForPattern>(DESCENDING_SORT);
|
||||||
r.put(cat, m);
|
r.put(cat, m);
|
||||||
}
|
}
|
||||||
if (r.get(cat).get(right.getRefPattern()) == null) {
|
if (r.get(cat).get(right.getRefPattern()) == null) {
|
||||||
|
|||||||
17
pom.xml
17
pom.xml
@@ -548,6 +548,12 @@ limitations under the License.
|
|||||||
<version>1.5.4</version>
|
<version>1.5.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-lang</groupId>
|
||||||
|
<artifactId>commons-lang</artifactId>
|
||||||
|
<version>2.5</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>eu.medsea.mimeutil</groupId>
|
<groupId>eu.medsea.mimeutil</groupId>
|
||||||
<artifactId>mime-util</artifactId>
|
<artifactId>mime-util</artifactId>
|
||||||
@@ -725,6 +731,12 @@ limitations under the License.
|
|||||||
<artifactId>juniversalchardet</artifactId>
|
<artifactId>juniversalchardet</artifactId>
|
||||||
<version>1.0.3</version>
|
<version>1.0.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>dk.brics.automaton</groupId>
|
||||||
|
<artifactId>automaton</artifactId>
|
||||||
|
<version>1.11.2</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
@@ -758,5 +770,10 @@ limitations under the License.
|
|||||||
<id>objectweb-repository</id>
|
<id>objectweb-repository</id>
|
||||||
<url>http://maven.objectweb.org/maven2/</url>
|
<url>http://maven.objectweb.org/maven2/</url>
|
||||||
</repository>
|
</repository>
|
||||||
|
|
||||||
|
<repository>
|
||||||
|
<id>clojars-repo</id>
|
||||||
|
<url>http://clojars.org/repo</url>
|
||||||
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
Reference in New Issue
Block a user