google-nomulus/java/google/registry/reporting/spec11/Spec11EmailUtils.java
gbrodman 2b12ca42f5 Use the registrar client ID and abuse email address in Spec11
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=244899045
2019-04-26 23:45:07 -04:00

195 lines
8 KiB
Java

// Copyright 2018 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.reporting.spec11;
import static com.google.common.base.Throwables.getRootCause;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.io.Resources.getResource;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger;
import com.google.common.net.MediaType;
import com.google.template.soy.SoyFileSet;
import com.google.template.soy.parseinfo.SoyTemplateInfo;
import com.google.template.soy.tofu.SoyTofu;
import com.google.template.soy.tofu.SoyTofu.Renderer;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarContact;
import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo;
import google.registry.util.EmailMessage;
import google.registry.util.SendEmailService;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import org.joda.time.LocalDate;
/** Provides e-mail functionality for Spec11 tasks, such as sending Spec11 reports to registrars. */
public class Spec11EmailUtils {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final SoyTofu SOY_SAUCE =
SoyFileSet.builder()
.add(
getResource(
Spec11EmailSoyInfo.getInstance().getClass(),
Spec11EmailSoyInfo.getInstance().getFileName()))
.build()
.compileToTofu();
private final SendEmailService emailService;
private final InternetAddress outgoingEmailAddress;
private final InternetAddress alertRecipientAddress;
private final InternetAddress spec11ReplyToAddress;
private final ImmutableList<String> spec11WebResources;
private final String registryName;
@Inject
Spec11EmailUtils(
SendEmailService emailService,
@Config("gSuiteOutgoingEmailAddress") InternetAddress outgoingEmailAddress,
@Config("alertRecipientEmailAddress") InternetAddress alertRecipientAddress,
@Config("spec11ReplyToEmailAddress") InternetAddress spec11ReplyToAddress,
@Config("spec11WebResources") ImmutableList<String> spec11WebResources,
@Config("registryName") String registryName) {
this.emailService = emailService;
this.outgoingEmailAddress = outgoingEmailAddress;
this.alertRecipientAddress = alertRecipientAddress;
this.spec11ReplyToAddress = spec11ReplyToAddress;
this.spec11WebResources = spec11WebResources;
this.registryName = registryName;
}
/**
* Processes a list of registrar/list-of-threat pairings and sends a notification email to the
* appropriate address.
*/
void emailSpec11Reports(
LocalDate date,
SoyTemplateInfo soyTemplateInfo,
String subject,
Set<RegistrarThreatMatches> registrarThreatMatchesSet) {
ImmutableMap.Builder<RegistrarThreatMatches, Throwable> failedMatchesBuilder =
ImmutableMap.builder();
for (RegistrarThreatMatches registrarThreatMatches : registrarThreatMatchesSet) {
try {
// Handle exceptions individually per registrar so that one failed email doesn't prevent the
// rest from being sent.
emailRegistrar(date, soyTemplateInfo, subject, registrarThreatMatches);
} catch (Throwable e) {
failedMatchesBuilder.put(registrarThreatMatches, getRootCause(e));
}
}
ImmutableMap<RegistrarThreatMatches, Throwable> failedMatches = failedMatchesBuilder.build();
if (!failedMatches.isEmpty()) {
ImmutableList<Map.Entry<RegistrarThreatMatches, Throwable>> failedMatchesList =
failedMatches.entrySet().asList();
// Send an alert email and throw a RuntimeException with the first failure as the cause,
// but log the rest so that we have that information.
Throwable firstThrowable = failedMatchesList.get(0).getValue();
sendAlertEmail(
String.format("Spec11 Emailing Failure %s", date),
String.format("Emailing Spec11 reports failed due to %s", firstThrowable.getMessage()));
for (int i = 1; i < failedMatches.size(); i++) {
logger.atSevere().withCause(failedMatchesList.get(i).getValue()).log(
"Additional exception thrown when sending email to registrar %s, in addition to the"
+ " re-thrown exception",
failedMatchesList.get(i).getKey().clientId());
}
throw new RuntimeException(
"Emailing Spec11 reports failed, first exception:", firstThrowable);
}
sendAlertEmail(
String.format("Spec11 Pipeline Success %s", date),
"Spec11 reporting completed successfully.");
}
private void emailRegistrar(
LocalDate date,
SoyTemplateInfo soyTemplateInfo,
String subject,
RegistrarThreatMatches registrarThreatMatches)
throws MessagingException {
emailService.sendEmail(
EmailMessage.newBuilder()
.setSubject(subject)
.setBody(getContent(date, soyTemplateInfo, registrarThreatMatches))
.setContentType(MediaType.HTML_UTF_8)
.setFrom(outgoingEmailAddress)
.addRecipient(getEmailAddressForRegistrar(registrarThreatMatches.clientId()))
.setBcc(spec11ReplyToAddress)
.build());
}
private String getContent(
LocalDate date,
SoyTemplateInfo soyTemplateInfo,
RegistrarThreatMatches registrarThreatMatches) {
Renderer renderer = SOY_SAUCE.newRenderer(soyTemplateInfo);
// Soy templates require that data be in raw map/list form.
List<Map<String, String>> threatMatchMap =
registrarThreatMatches.threatMatches().stream()
.map(
threatMatch ->
ImmutableMap.of(
"fullyQualifiedDomainName", threatMatch.fullyQualifiedDomainName(),
"threatType", threatMatch.threatType()))
.collect(toImmutableList());
Map<String, Object> data =
ImmutableMap.of(
"date", date.toString(),
"registry", registryName,
"replyToEmail", spec11ReplyToAddress.getAddress(),
"threats", threatMatchMap,
"resources", spec11WebResources);
renderer.setData(data);
return renderer.render();
}
/** Sends an e-mail indicating the state of the spec11 pipeline, with a given subject and body. */
void sendAlertEmail(String subject, String body) {
try {
emailService.sendEmail(
EmailMessage.newBuilder()
.setFrom(outgoingEmailAddress)
.addRecipient(alertRecipientAddress)
.setBody(body)
.setSubject(subject)
.build());
} catch (Throwable e) {
throw new RuntimeException("The spec11 alert e-mail system failed.", e);
}
}
private InternetAddress getEmailAddressForRegistrar(String clientId) throws MessagingException {
// Attempt to use the registrar's WHOIS abuse contact, then fall back to the regular address.
Registrar registrar =
Registrar.loadByClientIdCached(clientId)
.orElseThrow(
() ->
new IllegalArgumentException(
String.format("Could not find registrar %s", clientId)));
return new InternetAddress(
registrar
.getWhoisAbuseContact()
.map(RegistrarContact::getEmailAddress)
.orElse(registrar.getEmailAddress()));
}
}