Added OutgoingEmailValidationListener

This new extension API allows extensions to implement a low-level
e-mail filtering mechanism. Extensions may change the from, to,
headers, and body attributes of an outgoing e-mail.
Alternatively, a ValidationException may be thrown to cancel the
outgoing e-mail.

Optional high-level attributes, such as Change-Id, are found as
corresponding e-mail headers (X-Gerrit-Change-Id) or encoded in
the body.

Change-Id: I0d22b6eefcbd79115fb7c2827b8bf18eaeb1ad22
This commit is contained in:
Zalan Blenessy
2014-12-23 11:42:28 +01:00
parent 153597156f
commit 81671c9346
5 changed files with 94 additions and 2 deletions

View File

@@ -85,6 +85,14 @@ group names.
Plugins implementing the `HashtagValidationListener` interface can perform
validation of hashtags before they are added to or removed from changes.
[[outgoing-email-validation]]
== Outgoing e-mail validation
This interface provides a low-level e-mail filtering API for plugins.
Plugins implementing the `OutgoingEmailValidationListener` interface can perform
filtering of outgoing e-mails just before they are sent.
GERRIT
------

View File

@@ -130,6 +130,7 @@ import com.google.gerrit.server.util.IdGenerator;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.server.validators.GroupCreationValidationListener;
import com.google.gerrit.server.validators.HashtagValidationListener;
import com.google.gerrit.server.validators.OutgoingEmailValidationListener;
import com.google.gerrit.server.validators.ProjectCreationValidationListener;
import com.google.inject.Inject;
import com.google.inject.TypeLiteral;
@@ -277,6 +278,7 @@ public class GerritGlobalModule extends FactoryModule {
DynamicSet.setOf(binder(), ProjectCreationValidationListener.class);
DynamicSet.setOf(binder(), GroupCreationValidationListener.class);
DynamicSet.setOf(binder(), HashtagValidationListener.class);
DynamicSet.setOf(binder(), OutgoingEmailValidationListener.class);
DynamicItem.itemOf(binder(), AvatarProvider.class);
DynamicSet.setOf(binder(), LifecycleListener.class);
DynamicSet.setOf(binder(), TopMenu.class);

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.server.mail;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.ApprovalsUtil;
@@ -35,6 +36,7 @@ import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.ssh.SshAdvertisedAddresses;
import com.google.gerrit.server.validators.OutgoingEmailValidationListener;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -67,6 +69,7 @@ public class EmailArguments {
final ChangeData.Factory changeDataFactory;
final RuntimeInstance velocityRuntime;
final EmailSettings settings;
final DynamicSet<OutgoingEmailValidationListener> outgoingEmailValidationListeners;
@Inject
EmailArguments(GitRepositoryManager server, ProjectCache projectCache,
@@ -88,7 +91,8 @@ public class EmailArguments {
ChangeData.Factory changeDataFactory,
RuntimeInstance velocityRuntime,
EmailSettings settings,
@SshAdvertisedAddresses List<String> sshAddresses) {
@SshAdvertisedAddresses List<String> sshAddresses,
DynamicSet<OutgoingEmailValidationListener> outgoingEmailValidationListeners) {
this.server = server;
this.projectCache = projectCache;
this.groupBackend = groupBackend;
@@ -112,5 +116,6 @@ public class EmailArguments {
this.velocityRuntime = velocityRuntime;
this.settings = settings;
this.sshAddresses = sshAddresses;
this.outgoingEmailValidationListeners = outgoingEmailValidationListeners;
}
}

View File

@@ -21,6 +21,8 @@ import com.google.gerrit.reviewdb.client.UserIdentity;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.mail.EmailHeader.AddressList;
import com.google.gwtorm.server.OrmException;
import com.google.gerrit.server.validators.OutgoingEmailValidationListener;
import com.google.gerrit.server.validators.ValidationException;
import org.apache.commons.lang.StringUtils;
import org.apache.velocity.Template;
@@ -121,7 +123,21 @@ public abstract class OutgoingEmail {
}
}
args.emailSender.send(smtpFromAddress, smtpRcptTo, headers, body.toString());
OutgoingEmailValidationListener.Args va = new OutgoingEmailValidationListener.Args();
va.messageClass = messageClass;
va.smtpFromAddress = smtpFromAddress;
va.smtpRcptTo = smtpRcptTo;
va.headers = headers;
va.body = body.toString();
for (OutgoingEmailValidationListener validator : args.outgoingEmailValidationListeners) {
try {
validator.validateOutgoingEmail(va);
} catch (ValidationException e) {
return;
}
}
args.emailSender.send(va.smtpFromAddress, va.smtpRcptTo, va.headers, va.body);
}
}

View File

@@ -0,0 +1,61 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.validators;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
import com.google.gerrit.server.account.CreateGroupArgs;
import com.google.gerrit.server.mail.Address;
import com.google.gerrit.server.mail.EmailHeader;
import java.util.Map;
import java.util.Set;
/**
* Listener to provide validation on outgoing email notification.
*/
@ExtensionPoint
public interface OutgoingEmailValidationListener {
/**
* Arguments supplied to validateOutgoingEmail.
*/
public static class Args {
// in arguments
public String messageClass;
// in/out arguments
public Address smtpFromAddress;
public Set<Address> smtpRcptTo;
public String body;
public Map<String, EmailHeader> headers;
}
/**
* Outgoing e-mail validation.
*
* Invoked by Gerrit just before an e-mail is sent, after all e-mail templates
* have been applied.
*
* Plugins may modify the following fields in args:
* - smtpFromAddress
* - smtpRcptTo
* - body
* - headers
*
* @param args E-mail properties. Some are mutable.
* @throws ValidationException if validation fails.
*/
public void validateOutgoingEmail(OutgoingEmailValidationListener.Args args)
throws ValidationException;
}