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 483ecafbde..66476522a7 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 @@ -77,7 +77,7 @@ public class PermissionCollection { boolean perUser = false; Map sectionToProject = Maps.newLinkedHashMap(); - for (SectionMatcher matcher : matcherList) { + for (SectionMatcher sm : matcherList) { // If the matcher has to expand parameters and its prefix matches the // reference there is a very good chance the reference is actually user // specific, even if the matcher does not match the reference. Since its @@ -91,12 +91,12 @@ public class PermissionCollection { // references are usually less frequent than the non-user references. // if (username != null && !perUser - && matcher instanceof SectionMatcher.ExpandParameters) { - perUser = ((SectionMatcher.ExpandParameters) matcher).matchPrefix(ref); + && sm.matcher instanceof RefPatternMatcher.ExpandParameters) { + perUser = ((RefPatternMatcher.ExpandParameters) sm.matcher).matchPrefix(ref); } - if (matcher.match(ref, username)) { - sectionToProject.put(matcher.section, matcher.project); + if (sm.match(ref, username)) { + sectionToProject.put(sm.section, sm.project); } } List sections = Lists.newArrayList(sectionToProject.keySet()); 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 new file mode 100644 index 0000000000..b71d194479 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefPatternMatcher.java @@ -0,0 +1,122 @@ +// Copyright (C) 2013 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 static com.google.gerrit.server.project.RefControl.isRE; +import com.google.gerrit.common.data.ParameterizedString; +import dk.brics.automaton.Automaton; +import java.util.Collections; +import java.util.regex.Pattern; + +abstract class RefPatternMatcher { + public static RefPatternMatcher getMatcher(String pattern) { + if (pattern.contains("${")) { + return new ExpandParameters(pattern); + } else if (isRE(pattern)) { + return new Regexp(pattern); + } else if (pattern.endsWith("/*")) { + return new Prefix(pattern.substring(0, pattern.length() - 1)); + } else { + return new Exact(pattern); + } + } + + abstract boolean match(String ref, String username); + + private static class Exact extends RefPatternMatcher { + private final String expect; + + Exact(String name) { + expect = name; + } + + @Override + boolean match(String ref, String username) { + return expect.equals(ref); + } + } + + private static class Prefix extends RefPatternMatcher { + private final String prefix; + + Prefix(String pfx) { + prefix = pfx; + } + + @Override + boolean match(String ref, String username) { + return ref.startsWith(prefix); + } + } + + private static class Regexp extends RefPatternMatcher { + private final Pattern pattern; + + Regexp(String re) { + pattern = Pattern.compile(re); + } + + @Override + boolean match(String ref, String username) { + return pattern.matcher(ref).matches(); + } + } + + static class ExpandParameters extends RefPatternMatcher { + private final ParameterizedString template; + private final String prefix; + + ExpandParameters(String pattern) { + template = new ParameterizedString(pattern); + + if (isRE(pattern)) { + // Replace ${username} with ":USERNAME:" as : is not legal + // 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. + Automaton am = + RefControl.toRegExp( + template.replace(Collections.singletonMap("username", + ":USERNAME:"))).toAutomaton(); + String rePrefix = am.getCommonPrefix(); + prefix = rePrefix.substring(0, rePrefix.indexOf(":USERNAME:")); + } else { + prefix = pattern.substring(0, pattern.indexOf("${")); + } + } + + @Override + boolean match(String ref, String username) { + if (!ref.startsWith(prefix) || username == null) { + return false; + } + + String u; + if (isRE(template.getPattern())) { + u = username.replace(".", "\\."); + } else { + u = username; + } + + RefPatternMatcher next = + getMatcher(template.replace(Collections.singletonMap("username", u))); + return next != null ? next.match(ref, username) : false; + } + + boolean matchPrefix(String ref) { + return ref.startsWith(prefix); + } + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SectionMatcher.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SectionMatcher.java index 6f8af80064..44c8b9a915 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SectionMatcher.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SectionMatcher.java @@ -14,147 +14,38 @@ package com.google.gerrit.server.project; -import static com.google.gerrit.server.project.RefControl.isRE; - import com.google.gerrit.common.data.AccessSection; -import com.google.gerrit.common.data.ParameterizedString; import com.google.gerrit.reviewdb.client.Project; -import dk.brics.automaton.Automaton; - -import java.util.Collections; -import java.util.regex.Pattern; - /** * Matches an AccessSection against a reference name. *

* These matchers are "compiled" versions of the AccessSection name, supporting * faster selection of which sections are relevant to any given input reference. */ -abstract class SectionMatcher { +class SectionMatcher extends RefPatternMatcher { static SectionMatcher wrap(Project.NameKey project, AccessSection section) { String ref = section.getName(); if (AccessSection.isValid(ref)) { - return wrap(project, ref, section); + return new SectionMatcher(project, section, getMatcher(ref)); } else { return null; } } - static SectionMatcher wrap(Project.NameKey project, String pattern, - AccessSection section) { - if (pattern.contains("${")) { - return new ExpandParameters(project, pattern, section); - - } else if (isRE(pattern)) { - return new Regexp(project, pattern, section); - - } else if (pattern.endsWith("/*")) { - return new Prefix(project, pattern.substring(0, pattern.length() - 1), - section); - - } else { - return new Exact(project, pattern, section); - } - } - final Project.NameKey project; final AccessSection section; + final RefPatternMatcher matcher; - SectionMatcher(Project.NameKey project, AccessSection section) { + SectionMatcher(Project.NameKey project, AccessSection section, + RefPatternMatcher matcher) { this.project = project; this.section = section; + this.matcher = matcher; } - abstract boolean match(String ref, String username); - - private static class Exact extends SectionMatcher { - private final String expect; - - Exact(Project.NameKey project, String name, AccessSection section) { - super(project, section); - expect = name; - } - - @Override - boolean match(String ref, String username) { - return expect.equals(ref); - } - } - - private static class Prefix extends SectionMatcher { - private final String prefix; - - Prefix(Project.NameKey project, String pfx, AccessSection section) { - super(project, section); - prefix = pfx; - } - - @Override - boolean match(String ref, String username) { - return ref.startsWith(prefix); - } - } - - private static class Regexp extends SectionMatcher { - private final Pattern pattern; - - Regexp(Project.NameKey project, String re, AccessSection section) { - super(project, section); - pattern = Pattern.compile(re); - } - - @Override - boolean match(String ref, String username) { - return pattern.matcher(ref).matches(); - } - } - - static class ExpandParameters extends SectionMatcher { - private final ParameterizedString template; - private final String prefix; - - ExpandParameters(Project.NameKey project, String pattern, - AccessSection section) { - super(project, section); - template = new ParameterizedString(pattern); - - if (isRE(pattern)) { - // Replace ${username} with ":USERNAME:" as : is not legal - // 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. - Automaton am = RefControl.toRegExp( - template.replace(Collections.singletonMap("username", ":USERNAME:"))) - .toAutomaton(); - String rePrefix = am.getCommonPrefix(); - prefix = rePrefix.substring(0, rePrefix.indexOf(":USERNAME:")); - } else { - prefix = pattern.substring(0, pattern.indexOf("${")); - } - } - - @Override - boolean match(String ref, String username) { - if (!ref.startsWith(prefix) || username == null) { - return false; - } - - String u; - if (isRE(template.getPattern())) { - u = username.replace(".", "\\."); - } else { - u = username; - } - - SectionMatcher next = wrap(project, - template.replace(Collections.singletonMap("username", u)), - section); - return next != null ? next.match(ref, username) : false; - } - - boolean matchPrefix(String ref) { - return ref.startsWith(prefix); - } + @Override + boolean match(String ref, String username) { + return this.matcher.match(ref, username); } }