Add sendemail.denyrcpt functionality
Currently sendemail.allowrcpt exists in the configuration
to whitelist specific emails or domains.
This add sendemail.denyrcpt to blacklist specific emails
or domains.
Example usecase:
To be able to block e-mails from being sent to service users that
don't have an email and preventing log-spam such as:
* EmailException: Recipient address rejected: User unknown in local
recipient table
denyrcpt is evaluated first.
Neither allowrcpt or denyrcpt will prevent account
creation but both will prevent confirmation emails.
Change-Id: I97d6b378dd0678689f65c42e79de68162c889a92
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 167f1cb..2c1d146 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -4392,6 +4392,19 @@
If set to a domain name, any address at that domain can receive
email from Gerrit.
+
+If allowrcpt is configured, The set of allowed recipients is:
+`allowrcpt - denyrcpt`.
++
+By default, unset, permitting delivery to any email address.
+
+[[sendemail.denyrcpt]]sendemail.denyrcpt::
++
+If present, each value adds one entry to the blacklist of email
+addresses that Gerrit can send email to. If set to a complete
+email address, that one address is added to the blacklist.
+If set to a domain name, any address at that domain can *not* receive
+email from Gerrit.
++
By default, unset, permitting delivery to any email address.
[[sendemail.includeDiff]]sendemail.includeDiff::
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 615accc..bec8a4b 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -639,6 +639,9 @@
If link:config-gerrit.html#sendemail.allowrcpt[sendemail.allowrcpt] is
configured, the added email address must belong to a domain that is
allowed, unless `no_confirmation` is set.
+If link:config-gerrit.html#sendemail.denyrcpt[sendemail.denyrcpt]
+is configured, make sure that the added email address is *not* disallowed or
+belongs to a domain that is disallowed.
The link:#email-input[EmailInput] object in the request body may
contain additional options for the email address.
diff --git a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
index 400bc4f..664c1bf 100644
--- a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
+++ b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
@@ -504,9 +504,7 @@
if (addr != null && addr.getEmail() != null && addr.getEmail().length() > 0) {
if (!args.validator.isValid(addr.getEmail())) {
logger.atWarning().log("Not emailing %s (invalid email address)", addr.getEmail());
- } else if (!args.emailSender.canEmail(addr.getEmail())) {
- logger.atWarning().log("Not emailing %s (prohibited by allowrcpt)", addr.getEmail());
- } else {
+ } else if (args.emailSender.canEmail(addr.getEmail())) {
if (!smtpRcptTo.add(addr)) {
if (!override) {
return;
diff --git a/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java b/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
index 8615c04..a407cab 100644
--- a/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
+++ b/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
@@ -16,6 +16,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.common.flogger.FluentLogger;
import com.google.common.io.BaseEncoding;
import com.google.common.primitives.Ints;
import com.google.gerrit.common.Nullable;
@@ -56,6 +57,8 @@
/** The socket's connect timeout (0 = infinite timeout) */
private static final int DEFAULT_CONNECT_TIMEOUT = 0;
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
public static class Module extends AbstractModule {
@Override
protected void configure() {
@@ -73,6 +76,7 @@
private Encryption smtpEncryption;
private boolean sslVerify;
private Set<String> allowrcpt;
+ private Set<String> denyrcpt;
private String importance;
private int expiryDays;
@@ -117,6 +121,9 @@
Set<String> rcpt = new HashSet<>();
Collections.addAll(rcpt, cfg.getStringList("sendemail", null, "allowrcpt"));
allowrcpt = Collections.unmodifiableSet(rcpt);
+ Set<String> rcptdeny = new HashSet<>();
+ Collections.addAll(rcptdeny, cfg.getStringList("sendemail", null, "denyrcpt"));
+ denyrcpt = Collections.unmodifiableSet(rcptdeny);
importance = cfg.getString("sendemail", null, "importance");
expiryDays = cfg.getInt("sendemail", null, "expiryDays", 0);
}
@@ -129,22 +136,47 @@
@Override
public boolean canEmail(String address) {
if (!isEnabled()) {
+ logger.atWarning().log("Not emailing %s (email is disabled)", address);
return false;
}
+ String domain = address.substring(address.lastIndexOf('@') + 1);
+ if (isDenied(address, domain)) {
+ return false;
+ }
+
+ return isAllowed(address, domain);
+ }
+
+ private boolean isDenied(String address, String domain) {
+
+ if (denyrcpt.isEmpty()) {
+ return false;
+ }
+
+ if (denyrcpt.contains(address)
+ || denyrcpt.contains(domain)
+ || denyrcpt.contains("@" + domain)) {
+ logger.atWarning().log("Not emailing %s (prohibited by sendemail.denyrcpt)", address);
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean isAllowed(String address, String domain) {
+
if (allowrcpt.isEmpty()) {
return true;
}
- if (allowrcpt.contains(address)) {
+ if (allowrcpt.contains(address)
+ || allowrcpt.contains(domain)
+ || allowrcpt.contains("@" + domain)) {
return true;
}
- String domain = address.substring(address.lastIndexOf('@') + 1);
- if (allowrcpt.contains(domain) || allowrcpt.contains("@" + domain)) {
- return true;
- }
-
+ logger.atWarning().log("Not emailing %s (prohibited by sendemail.allowrcpt)", address);
return false;
}