LCOV - code coverage report
Current view: top level - server/project - RefPatternMatcher.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 66 68 97.1 %
Date: 2022-11-19 15:00:39 Functions: 16 16 100.0 %

          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             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750