LCOV - code coverage report
Current view: top level - server/mail/send - FromAddressGeneratorProvider.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 68 79 86.1 %
Date: 2022-11-19 15:00:39 Functions: 12 15 80.0 %

          Line data    Source code
       1             : // Copyright (C) 2009 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.mail.send;
      16             : 
      17             : import static java.nio.charset.StandardCharsets.UTF_8;
      18             : 
      19             : import com.google.common.io.BaseEncoding;
      20             : import com.google.gerrit.common.data.ParameterizedString;
      21             : import com.google.gerrit.entities.Account;
      22             : import com.google.gerrit.entities.Address;
      23             : import com.google.gerrit.server.GerritPersonIdent;
      24             : import com.google.gerrit.server.account.AccountCache;
      25             : import com.google.gerrit.server.account.AccountState;
      26             : import com.google.gerrit.server.config.AnonymousCowardName;
      27             : import com.google.gerrit.server.config.GerritServerConfig;
      28             : import com.google.gerrit.server.mail.MailUtil;
      29             : import com.google.inject.Inject;
      30             : import com.google.inject.Provider;
      31             : import com.google.inject.Singleton;
      32             : import java.security.MessageDigest;
      33             : import java.security.NoSuchAlgorithmException;
      34             : import java.util.Optional;
      35             : import java.util.regex.Pattern;
      36             : import org.eclipse.jgit.lib.Config;
      37             : import org.eclipse.jgit.lib.PersonIdent;
      38             : 
      39             : /** Creates a {@link FromAddressGenerator} from the {@link GerritServerConfig} */
      40             : @Singleton
      41             : public class FromAddressGeneratorProvider implements Provider<FromAddressGenerator> {
      42             :   private final FromAddressGenerator generator;
      43             : 
      44             :   @Inject
      45             :   FromAddressGeneratorProvider(
      46             :       @GerritServerConfig Config cfg,
      47             :       @AnonymousCowardName String anonymousCowardName,
      48             :       @GerritPersonIdent PersonIdent myIdent,
      49         146 :       AccountCache accountCache) {
      50         146 :     final String from = cfg.getString("sendemail", null, "from");
      51         146 :     final Address srvAddr = toAddress(myIdent);
      52             : 
      53         146 :     if (from == null || "MIXED".equalsIgnoreCase(from)) {
      54         146 :       ParameterizedString name = new ParameterizedString("${user} (Code Review)");
      55         146 :       generator = new PatternGen(srvAddr, accountCache, anonymousCowardName, name, srvAddr.email());
      56         146 :     } else if ("USER".equalsIgnoreCase(from)) {
      57           1 :       String[] domains = cfg.getStringList("sendemail", null, "allowedDomain");
      58           1 :       Pattern domainPattern = MailUtil.glob(domains);
      59           1 :       ParameterizedString namePattern = new ParameterizedString("${user} (Code Review)");
      60           1 :       generator =
      61             :           new UserGen(accountCache, domainPattern, anonymousCowardName, namePattern, srvAddr);
      62           1 :     } else if ("SERVER".equalsIgnoreCase(from)) {
      63           1 :       generator = new ServerGen(srvAddr);
      64             :     } else {
      65           1 :       final Address a = Address.parse(from);
      66           1 :       final ParameterizedString name = a.name() != null ? new ParameterizedString(a.name()) : null;
      67           1 :       if (name == null || name.getParameterNames().isEmpty()) {
      68           0 :         generator = new ServerGen(a);
      69             :       } else {
      70           1 :         generator = new PatternGen(srvAddr, accountCache, anonymousCowardName, name, a.email());
      71             :       }
      72             :     }
      73         146 :   }
      74             : 
      75             :   private static Address toAddress(PersonIdent myIdent) {
      76         146 :     return Address.create(myIdent.getName(), myIdent.getEmailAddress());
      77             :   }
      78             : 
      79             :   @Override
      80             :   public FromAddressGenerator get() {
      81         146 :     return generator;
      82             :   }
      83             : 
      84             :   static final class UserGen implements FromAddressGenerator {
      85             :     private final AccountCache accountCache;
      86             :     private final Pattern domainPattern;
      87             :     private final String anonymousCowardName;
      88             :     private final ParameterizedString nameRewriteTmpl;
      89             :     private final Address serverAddress;
      90             : 
      91             :     /**
      92             :      * From address generator for USER mode
      93             :      *
      94             :      * @param accountCache get user account from id
      95             :      * @param domainPattern allowed user domain pattern that Gerrit can send as the user
      96             :      * @param anonymousCowardName name used when user's full name is missing
      97             :      * @param nameRewriteTmpl name template used for rewriting the sender's name when Gerrit can not
      98             :      *     send as the user
      99             :      * @param serverAddress serverAddress.name is used when fromId is null and serverAddress.email
     100             :      *     is used when Gerrit can not send as the user
     101             :      */
     102             :     UserGen(
     103             :         AccountCache accountCache,
     104             :         Pattern domainPattern,
     105             :         String anonymousCowardName,
     106             :         ParameterizedString nameRewriteTmpl,
     107           1 :         Address serverAddress) {
     108           1 :       this.accountCache = accountCache;
     109           1 :       this.domainPattern = domainPattern;
     110           1 :       this.anonymousCowardName = anonymousCowardName;
     111           1 :       this.nameRewriteTmpl = nameRewriteTmpl;
     112           1 :       this.serverAddress = serverAddress;
     113           1 :     }
     114             : 
     115             :     @Override
     116             :     public boolean isGenericAddress(Account.Id fromId) {
     117           0 :       return false;
     118             :     }
     119             : 
     120             :     @Override
     121             :     public Address from(Account.Id fromId) {
     122             :       String senderName;
     123           1 :       if (fromId != null) {
     124           1 :         Optional<Account> a = accountCache.get(fromId).map(AccountState::account);
     125           1 :         String fullName = a.map(Account::fullName).orElse(null);
     126           1 :         String userEmail = a.map(Account::preferredEmail).orElse(null);
     127           1 :         if (canRelay(userEmail)) {
     128           1 :           return Address.create(fullName, userEmail);
     129             :         }
     130             : 
     131           1 :         if (fullName == null || "".equals(fullName.trim())) {
     132           0 :           fullName = anonymousCowardName;
     133             :         }
     134           1 :         senderName = nameRewriteTmpl.replace("user", fullName).toString();
     135           1 :       } else {
     136           1 :         senderName = serverAddress.name();
     137             :       }
     138             : 
     139             :       String senderEmail;
     140           1 :       ParameterizedString senderEmailPattern = new ParameterizedString(serverAddress.email());
     141           1 :       if (senderEmailPattern.getParameterNames().isEmpty()) {
     142           1 :         senderEmail = senderEmailPattern.getRawPattern();
     143             :       } else {
     144           0 :         senderEmail = senderEmailPattern.replace("userHash", hashOf(senderName)).toString();
     145             :       }
     146           1 :       return Address.create(senderName, senderEmail);
     147             :     }
     148             : 
     149             :     /** check if Gerrit is allowed to send from {@code userEmail}. */
     150             :     private boolean canRelay(String userEmail) {
     151           1 :       if (userEmail != null) {
     152           1 :         int index = userEmail.indexOf('@');
     153           1 :         if (index > 0 && index < userEmail.length() - 1) {
     154           1 :           return domainPattern.matcher(userEmail.substring(index + 1)).matches();
     155             :         }
     156             :       }
     157           1 :       return false;
     158             :     }
     159             :   }
     160             : 
     161             :   static final class ServerGen implements FromAddressGenerator {
     162             :     private final Address srvAddr;
     163             : 
     164           1 :     ServerGen(Address srvAddr) {
     165           1 :       this.srvAddr = srvAddr;
     166           1 :     }
     167             : 
     168             :     @Override
     169             :     public boolean isGenericAddress(Account.Id fromId) {
     170           0 :       return true;
     171             :     }
     172             : 
     173             :     @Override
     174             :     public Address from(Account.Id fromId) {
     175           1 :       return srvAddr;
     176             :     }
     177             :   }
     178             : 
     179             :   static final class PatternGen implements FromAddressGenerator {
     180             :     private final ParameterizedString senderEmailPattern;
     181             :     private final Address serverAddress;
     182             :     private final AccountCache accountCache;
     183             :     private final String anonymousCowardName;
     184             :     private final ParameterizedString namePattern;
     185             : 
     186             :     PatternGen(
     187             :         final Address serverAddress,
     188             :         final AccountCache accountCache,
     189             :         final String anonymousCowardName,
     190             :         final ParameterizedString namePattern,
     191         146 :         final String senderEmail) {
     192         146 :       this.senderEmailPattern = new ParameterizedString(senderEmail);
     193         146 :       this.serverAddress = serverAddress;
     194         146 :       this.accountCache = accountCache;
     195         146 :       this.anonymousCowardName = anonymousCowardName;
     196         146 :       this.namePattern = namePattern;
     197         146 :     }
     198             : 
     199             :     @Override
     200             :     public boolean isGenericAddress(Account.Id fromId) {
     201         103 :       return false;
     202             :     }
     203             : 
     204             :     @Override
     205             :     public Address from(Account.Id fromId) {
     206             :       final String senderName;
     207             : 
     208         106 :       if (fromId != null) {
     209         103 :         String fullName = accountCache.get(fromId).map(a -> a.account().fullName()).orElse(null);
     210         103 :         if (fullName == null || "".equals(fullName)) {
     211          13 :           fullName = anonymousCowardName;
     212             :         }
     213         103 :         senderName = namePattern.replace("user", fullName).toString();
     214             : 
     215         103 :       } else {
     216          13 :         senderName = serverAddress.name();
     217             :       }
     218             : 
     219             :       String senderEmail;
     220         106 :       if (senderEmailPattern.getParameterNames().isEmpty()) {
     221         106 :         senderEmail = senderEmailPattern.getRawPattern();
     222             :       } else {
     223           0 :         senderEmail = senderEmailPattern.replace("userHash", hashOf(senderName)).toString();
     224             :       }
     225         106 :       return Address.create(senderName, senderEmail);
     226             :     }
     227             :   }
     228             : 
     229             :   private static String hashOf(String data) {
     230             :     try {
     231           0 :       MessageDigest hash = MessageDigest.getInstance("MD5");
     232           0 :       byte[] bytes = hash.digest(data.getBytes(UTF_8));
     233           0 :       return BaseEncoding.base64Url().encode(bytes);
     234           0 :     } catch (NoSuchAlgorithmException e) {
     235           0 :       throw new IllegalStateException("No MD5 available", e);
     236             :     }
     237             :   }
     238             : }

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