Line data Source code
1 : // Copyright (C) 2021 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.util; 16 : 17 : import com.google.common.base.Strings; 18 : import com.google.common.collect.ImmutableSet; 19 : import com.google.common.flogger.FluentLogger; 20 : import com.google.gerrit.entities.Account; 21 : import com.google.gerrit.entities.AttentionSetUpdate; 22 : import com.google.gerrit.entities.ChangeMessage; 23 : import com.google.gerrit.server.account.AccountCache; 24 : import com.google.gerrit.server.account.AccountState; 25 : import com.google.inject.Inject; 26 : import com.google.inject.Singleton; 27 : import java.util.Optional; 28 : import java.util.regex.Matcher; 29 : import java.util.regex.Pattern; 30 : 31 : /** 32 : * Utility functions for text that can be persisted in data storage and should not contain user 33 : * identifiable information, e.g. {@link ChangeMessage} or {@link AttentionSetUpdate#reason}. 34 : */ 35 : @Singleton 36 : public class AccountTemplateUtil { 37 : 38 : /** 39 : * Template to represent account in pseudonymized form in text, that might be persisted in data 40 : * storage. 41 : */ 42 : public static final String ACCOUNT_TEMPLATE = "<GERRIT_ACCOUNT_%d>"; 43 : 44 : public static final String ACCOUNT_TEMPLATE_REGEX = "<GERRIT_ACCOUNT_([0-9]+)>"; 45 : 46 147 : public static final Pattern ACCOUNT_TEMPLATE_PATTERN = Pattern.compile(ACCOUNT_TEMPLATE_REGEX); 47 : 48 147 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 49 : 50 : private final AccountCache accountCache; 51 : 52 : @Inject 53 146 : AccountTemplateUtil(AccountCache accountCache) { 54 146 : this.accountCache = accountCache; 55 146 : } 56 : 57 : /** Returns account ids that are used in text, that might contain {@link #ACCOUNT_TEMPLATE}. */ 58 : public static ImmutableSet<Account.Id> parseTemplates(String textTemplate) { 59 103 : if (Strings.isNullOrEmpty(textTemplate)) { 60 0 : return ImmutableSet.of(); 61 : } 62 103 : Matcher matcher = ACCOUNT_TEMPLATE_PATTERN.matcher(textTemplate); 63 103 : ImmutableSet.Builder<Account.Id> accountsInTemplate = ImmutableSet.builder(); 64 103 : while (matcher.find()) { 65 19 : String accountId = matcher.group(1); 66 19 : Optional<Account.Id> parsedAccountId = Account.Id.tryParse(accountId); 67 19 : if (parsedAccountId.isPresent()) { 68 19 : accountsInTemplate.add(parsedAccountId.get()); 69 : } else { 70 0 : logger.atFine().log("Failed to parse accountId from template %s", matcher.group()); 71 : } 72 19 : } 73 103 : return accountsInTemplate.build(); 74 : } 75 : 76 : public static String getAccountTemplate(Account.Id accountId) { 77 22 : return String.format(ACCOUNT_TEMPLATE, accountId.get()); 78 : } 79 : 80 : /** Builds user-readable text from text, that might contain {@link #ACCOUNT_TEMPLATE}. */ 81 : public String replaceTemplates(String messageTemplate) { 82 103 : Matcher matcher = ACCOUNT_TEMPLATE_PATTERN.matcher(messageTemplate); 83 103 : StringBuilder out = new StringBuilder(); 84 103 : while (matcher.find()) { 85 19 : String accountId = matcher.group(1); 86 19 : String unrecognizedAccount = "Unrecognized Gerrit Account " + accountId; 87 19 : Optional<Account.Id> parsedAccountId = Account.Id.tryParse(accountId); 88 19 : if (parsedAccountId.isPresent()) { 89 19 : Optional<AccountState> account = accountCache.get(parsedAccountId.get()); 90 19 : if (account.isPresent()) { 91 19 : matcher.appendReplacement(out, account.get().account().getNameEmail(unrecognizedAccount)); 92 19 : continue; 93 : } 94 : } 95 0 : matcher.appendReplacement(out, unrecognizedAccount); 96 0 : } 97 103 : matcher.appendTail(out); 98 103 : return out.toString(); 99 : } 100 : }