Line data Source code
1 : // Copyright (C) 2013 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.project; 16 : 17 : import static com.google.common.collect.ImmutableSet.toImmutableSet; 18 : import static com.google.gerrit.server.project.RefPattern.containsParameters; 19 : import static com.google.gerrit.server.project.RefPattern.isRE; 20 : 21 : import com.google.common.collect.ImmutableMap; 22 : import com.google.common.collect.ImmutableSet; 23 : import com.google.common.collect.Streams; 24 : import com.google.gerrit.common.data.ParameterizedString; 25 : import com.google.gerrit.entities.AccessSection; 26 : import com.google.gerrit.entities.Account; 27 : import com.google.gerrit.entities.RefNames; 28 : import com.google.gerrit.server.CurrentUser; 29 : import dk.brics.automaton.Automaton; 30 : import java.util.HashMap; 31 : import java.util.Map; 32 : import java.util.regex.Pattern; 33 : import java.util.stream.Stream; 34 : 35 145 : public abstract class RefPatternMatcher { 36 : public static RefPatternMatcher getMatcher(AccessSection section) { 37 145 : if (section.getNamePattern().isPresent()) { 38 1 : return new Regexp(section.getNamePattern().get()); 39 : } 40 145 : return getMatcher(section.getName()); 41 : } 42 : 43 : public static RefPatternMatcher getMatcher(String pattern) { 44 145 : if (containsParameters(pattern)) { 45 52 : return new ExpandParameters(pattern); 46 145 : } else if (isRE(pattern)) { 47 1 : return new Regexp(pattern); 48 145 : } else if (pattern.endsWith("/*")) { 49 145 : return new Prefix(pattern.substring(0, pattern.length() - 1)); 50 : } else { 51 145 : return new Exact(pattern); 52 : } 53 : } 54 : 55 : public abstract boolean match(String ref, CurrentUser user); 56 : 57 : private static class Exact extends RefPatternMatcher { 58 : private final String expect; 59 : 60 145 : Exact(String name) { 61 145 : expect = name; 62 145 : } 63 : 64 : @Override 65 : public boolean match(String ref, CurrentUser user) { 66 145 : return expect.equals(ref); 67 : } 68 : } 69 : 70 : private static class Prefix extends RefPatternMatcher { 71 : private final String prefix; 72 : 73 145 : Prefix(String pfx) { 74 145 : prefix = pfx; 75 145 : } 76 : 77 : @Override 78 : public boolean match(String ref, CurrentUser user) { 79 145 : return ref.startsWith(prefix); 80 : } 81 : } 82 : 83 : private static class Regexp extends RefPatternMatcher { 84 : private final Pattern pattern; 85 : 86 1 : Regexp(String re) { 87 1 : pattern = Pattern.compile(re); 88 1 : } 89 : 90 1 : Regexp(Pattern re) { 91 1 : pattern = re; 92 1 : } 93 : 94 : @Override 95 : public boolean match(String ref, CurrentUser user) { 96 1 : return pattern.matcher(ref).matches() || (isRE(ref) && pattern.pattern().equals(ref)); 97 : } 98 : } 99 : 100 : public static class ExpandParameters extends RefPatternMatcher { 101 : private final ParameterizedString template; 102 : private final String prefix; 103 : 104 52 : ExpandParameters(String pattern) { 105 52 : template = new ParameterizedString(pattern); 106 : 107 52 : if (isRE(pattern)) { 108 : // Replace ${username} and ${shardeduserid} with ":PLACEHOLDER:" 109 : // as : is not legal in a reference and the string :PLACEHOLDER: 110 : // is not likely to be a valid part of the regex. This later 111 : // allows the pattern prefix to be clipped, saving time on 112 : // evaluation. 113 1 : String replacement = ":PLACEHOLDER:"; 114 1 : Map<String, String> params = 115 1 : ImmutableMap.of( 116 : RefPattern.USERID_SHARDED, replacement, 117 : RefPattern.USERNAME, replacement); 118 1 : Automaton am = RefPattern.toRegExp(template.replace(params)).toAutomaton(); 119 1 : String rePrefix = am.getCommonPrefix(); 120 1 : prefix = rePrefix.substring(0, rePrefix.indexOf(replacement)); 121 1 : } else { 122 52 : prefix = pattern.substring(0, pattern.indexOf("${")); 123 : } 124 52 : } 125 : 126 : @Override 127 : public boolean match(String ref, CurrentUser user) { 128 47 : if (isRE(ref)) { 129 1 : if (!ref.substring(1).startsWith(prefix)) { 130 0 : return false; 131 : } 132 47 : } else if (!ref.startsWith(prefix)) { 133 0 : return false; 134 : } 135 : 136 47 : for (String username : getUsernames(user)) { 137 : String u; 138 47 : if (isRE(template.getPattern())) { 139 1 : u = Pattern.quote(username); 140 : } else { 141 47 : u = username; 142 : } 143 : 144 47 : Account.Id accountId = user.isIdentifiedUser() ? user.getAccountId() : null; 145 47 : RefPatternMatcher next = getMatcher(expand(template, u, accountId)); 146 47 : if (next != null && next.match(expand(ref, u, accountId), user)) { 147 21 : return true; 148 : } 149 44 : } 150 44 : return false; 151 : } 152 : 153 : private ImmutableSet<String> getUsernames(CurrentUser user) { 154 47 : Stream<String> usernames = Streams.stream(user.getUserName()); 155 47 : if (user.isIdentifiedUser()) { 156 46 : usernames = Streams.concat(usernames, user.asIdentifiedUser().getEmailAddresses().stream()); 157 : } 158 47 : return usernames.collect(toImmutableSet()); 159 : } 160 : 161 : public boolean matchPrefix(String ref) { 162 52 : if (isRE(ref)) { 163 1 : return ref.substring(1).startsWith(prefix); 164 : } 165 52 : return ref.startsWith(prefix); 166 : } 167 : 168 : private String expand(String parameterizedRef, String userName, Account.Id accountId) { 169 47 : if (parameterizedRef.contains("${")) { 170 7 : return expand(new ParameterizedString(parameterizedRef), userName, accountId); 171 : } 172 47 : return parameterizedRef; 173 : } 174 : 175 : private String expand( 176 : ParameterizedString parameterizedRef, String userName, Account.Id accountId) { 177 47 : Map<String, String> params = new HashMap<>(); 178 47 : params.put(RefPattern.USERNAME, userName); 179 47 : if (accountId != null) { 180 46 : params.put(RefPattern.USERID_SHARDED, RefNames.shard(accountId.get())); 181 : } 182 47 : return parameterizedRef.replace(params); 183 : } 184 : } 185 : }