diff options
author | Øyvind Grønnesby <oyving@yahooinc.com> | 2022-11-23 19:47:58 +0100 |
---|---|---|
committer | Øyvind Grønnesby <oyving@yahooinc.com> | 2022-11-23 19:47:58 +0100 |
commit | 8c9dccb21e1d2915468b98ebba88ad8aa1437183 (patch) | |
tree | 2ece47faf72a545f01ac47075f20fd18bf1c7370 /controller-server | |
parent | e24e240f01de0c291eb62ab03718fbda6abdb1b2 (diff) |
Move mail verification templates to controller-server
Diffstat (limited to 'controller-server')
4 files changed, 519 insertions, 6 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java index b40db745fd7..9a7fffd2a9e 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java @@ -126,7 +126,7 @@ public class Controller extends AbstractComponent { notifier = new Notifier(curator, serviceRegistry.zoneRegistry(), serviceRegistry.mailer(), flagSource); notificationsDb = new NotificationsDb(this); supportAccessControl = new SupportAccessControl(this); - mailVerifier = new MailVerifier(tenantController, serviceRegistry.mailer(), curator, clock); + mailVerifier = new MailVerifier(serviceRegistry.zoneRegistry().dashboardUrl(), tenantController, serviceRegistry.mailer(), curator, clock); // Record the version of this controller curator().writeControllerVersion(this.hostname(), serviceRegistry.controllerVersion()); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/MailVerifier.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/MailVerifier.java index a7f3a3ca3b2..902343d5acf 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/MailVerifier.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/MailVerifier.java @@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.controller; import com.yahoo.config.provision.TenantName; +import com.yahoo.vespa.hosted.controller.api.integration.organization.Mail; import com.yahoo.vespa.hosted.controller.api.integration.organization.Mailer; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; @@ -10,11 +11,15 @@ import com.yahoo.vespa.hosted.controller.tenant.TenantContacts; import com.yahoo.vespa.hosted.controller.tenant.TenantInfo; import com.yahoo.vespa.hosted.controller.tenant.PendingMailVerification; +import java.net.URI; import java.time.Clock; import java.time.Duration; +import java.util.List; import java.util.Optional; import java.util.UUID; +import static com.yahoo.yolean.Exceptions.uncheck; + /** * @author olaa @@ -25,13 +30,16 @@ public class MailVerifier { private final Mailer mailer; private final CuratorDb curatorDb; private final Clock clock; + private final URI dashboardUri; private static final Duration VERIFICATION_DEADLINE = Duration.ofDays(7); - public MailVerifier(TenantController tenantController, Mailer mailer, CuratorDb curatorDb, Clock clock) { + + public MailVerifier(URI dashboardUri, TenantController tenantController, Mailer mailer, CuratorDb curatorDb, Clock clock) { this.tenantController = tenantController; this.mailer = mailer; this.curatorDb = curatorDb; this.clock = clock; + this.dashboardUri = dashboardUri; } public PendingMailVerification sendMailVerification(TenantName tenantName, String email, PendingMailVerification.MailType mailType) { @@ -43,7 +51,7 @@ public class MailVerifier { var verificationDeadline = clock.instant().plus(VERIFICATION_DEADLINE); var pendingMailVerification = new PendingMailVerification(tenantName, email, verificationCode, verificationDeadline, mailType); writePendingVerification(pendingMailVerification); - mailer.sendVerificationMail(pendingMailVerification); + mailer.send(mailOf(pendingMailVerification)); return pendingMailVerification; } @@ -113,4 +121,15 @@ public class MailVerifier { .map(CloudTenant.class::cast) .orElseThrow(() -> new IllegalStateException("Mail verification is only applicable for cloud tenants")); } + + private Mail mailOf(PendingMailVerification pendingMailVerification) { + var classLoader = this.getClass().getClassLoader(); + var template = uncheck(() -> classLoader.getResourceAsStream("mail/mail-verification.tmpl").readAllBytes()); + var message = new String(template) + .replaceAll("%\\{consoleUrl}", dashboardUri.getHost()) + .replaceAll("%\\{email}", pendingMailVerification.getMailAddress()) + .replaceAll("%\\{code}", pendingMailVerification.getVerificationCode()); + return new Mail(List.of(pendingMailVerification.getMailAddress()), "Please verify your email", "", message); + } + } diff --git a/controller-server/src/main/resources/mail/mail-verification.tmpl b/controller-server/src/main/resources/mail/mail-verification.tmpl new file mode 100644 index 00000000000..8a473e74755 --- /dev/null +++ b/controller-server/src/main/resources/mail/mail-verification.tmpl @@ -0,0 +1,494 @@ +<!DOCTYPE html> +<html + xmlns="http://www.w3.org/1999/xhtml" + xmlns:v="urn:schemas-microsoft-com:vml" + xmlns:o="urn:schemas-microsoft-com:office:office" +> + <head> + <title></title> + <!--[if !mso]><!--> + <meta http-equiv="X-UA-Compatible" content="IE=edge" /> + <!--<![endif]--> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta name="viewport" content="width=device-width,initial-scale=1" /> + <style type="text/css"> + #outlook a { + padding: 0; + } + + body { + margin: 0; + padding: 0; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + } + + table, + td { + border-collapse: collapse; + mso-table-lspace: 0pt; + mso-table-rspace: 0pt; + } + + img { + border: 0; + height: auto; + line-height: 100%; + outline: none; + text-decoration: none; + -ms-interpolation-mode: bicubic; + } + + p { + display: block; + margin: 13px 0; + } + </style> + <!--[if mso]> + <noscript> + <xml> + <o:OfficeDocumentSettings> + <o:AllowPNG /> + <o:PixelsPerInch>96</o:PixelsPerInch> + </o:OfficeDocumentSettings> + </xml> + </noscript> + <![endif]--> + <!--[if lte mso 11]> + <style type="text/css"> + .mj-outlook-group-fix { + width: 100% !important; + } + </style> + <![endif]--> + <!--[if !mso]><!--> + <link + href="https://fonts.googleapis.com/css?family=Open Sans" + rel="stylesheet" + type="text/css" + /> + <style type="text/css"> + @import url(https://fonts.googleapis.com/css?family=Open Sans); + </style> + <!--<![endif]--> + <style type="text/css"> + @media only screen and (min-width: 480px) { + .mj-column-per-100 { + width: 100% !important; + max-width: 100%; + } + } + </style> + <style media="screen and (min-width:480px)"> + .moz-text-html .mj-column-per-100 { + width: 100% !important; + max-width: 100%; + } + </style> + <style type="text/css"> + [owa] .mj-column-per-100 { + width: 100% !important; + max-width: 100%; + } + </style> + <style type="text/css"> + @media only screen and (max-width: 480px) { + table.mj-full-width-mobile { + width: 100% !important; + } + + td.mj-full-width-mobile { + width: auto !important; + } + } + </style> + </head> + + <body style="word-spacing: normal; background-color: #f8f8f8"> + <div style="background-color: #f8f8f8"> + <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--> + <div + style=" + background: #ffffff; + background-color: #ffffff; + margin: 0px auto; + max-width: 600px; + " + > + <table + align="center" + border="0" + cellpadding="0" + cellspacing="0" + role="presentation" + style="background: #ffffff; background-color: #ffffff; width: 100%" + > + <tbody> + <tr> + <td + style=" + direction: ltr; + font-size: 0px; + padding: 20px 0; + padding-bottom: 0px; + padding-left: 0px; + padding-right: 0px; + padding-top: 0px; + text-align: center; + " + > + <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]--> + <div + class="mj-column-per-100 mj-outlook-group-fix" + style=" + font-size: 0px; + text-align: left; + direction: ltr; + display: inline-block; + vertical-align: top; + width: 100%; + " + > + <table + border="0" + cellpadding="0" + cellspacing="0" + role="presentation" + style="vertical-align: top" + width="100%" + > + <tbody> + <tr> + <td + align="center" + style=" + font-size: 0px; + padding: 10px 25px; + padding-top: 0px; + padding-right: 0px; + padding-bottom: 40px; + padding-left: 0px; + word-break: break-word; + " + > + <p + style=" + border-top: solid 7px #3b9fde; + font-size: 1px; + margin: 0px auto; + width: 100%; + " + ></p> + <!--[if mso | IE + ]><table + align="center" + border="0" + cellpadding="0" + cellspacing="0" + style=" + border-top: solid 7px #3b9fde; + font-size: 1px; + margin: 0px auto; + width: 600px; + " + role="presentation" + width="600px" + > + <tr> + <td style="height: 0; line-height: 0"> + + </td> + </tr> + </table><! + [endif]--> + </td> + </tr> + <tr> + <td + align="center" + style=" + font-size: 0px; + padding: 10px 25px; + padding-top: 0px; + padding-bottom: 0px; + word-break: break-word; + " + > + <table + border="0" + cellpadding="0" + cellspacing="0" + role="presentation" + style=" + border-collapse: collapse; + border-spacing: 0px; + " + > + <tbody> + <tr> + <td style="width: 110px"> + <img + alt="" + height="auto" + src="https://data.vespa.oath.cloud/assets/vespa-icon.png" + style=" + border: none; + display: block; + outline: none; + text-decoration: none; + height: auto; + width: 100%; + font-size: 13px; + " + width="110" + /> + </td> + </tr> + </tbody> + </table> + </td> + </tr> + </tbody> + </table> + </div> + <!--[if mso | IE]></td></tr></table><![endif]--> + </td> + </tr> + </tbody> + </table> + </div> + <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--> + <div + style=" + background: #ffffff; + background-color: #ffffff; + margin: 0px auto; + max-width: 600px; + " + > + <table + align="center" + border="0" + cellpadding="0" + cellspacing="0" + role="presentation" + style="background: #ffffff; background-color: #ffffff; width: 100%" + > + <tbody> + <tr> + <td + style=" + direction: ltr; + font-size: 0px; + padding: 20px 0px 20px 0px; + padding-bottom: 70px; + padding-top: 30px; + text-align: center; + " + > + <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]--> + <div + class="mj-column-per-100 mj-outlook-group-fix" + style=" + font-size: 0px; + text-align: left; + direction: ltr; + display: inline-block; + vertical-align: top; + width: 100%; + " + > + <table + border="0" + cellpadding="0" + cellspacing="0" + role="presentation" + style="vertical-align: top" + width="100%" + > + <tbody> + <tr> + <td + align="left" + style=" + font-size: 0px; + padding: 0px 25px 0px 25px; + padding-top: 0px; + padding-right: 50px; + padding-bottom: 0px; + padding-left: 50px; + word-break: break-word; + " + > + <div + style=" + font-family: Open Sans, Helvetica, Arial, + sans-serif; + font-size: 13px; + line-height: 22px; + text-align: left; + color: #797e82; + " + > + <h1 + style=" + text-align: center; + color: #000000; + line-height: 32px; + " + > + Verify your email address + </h1> + </div> + </td> + </tr> + <tr> + <td + align="left" + style=" + font-size: 0px; + padding: 0px 25px 0px 25px; + padding-top: 0px; + padding-right: 50px; + padding-bottom: 0px; + padding-left: 50px; + word-break: break-word; + " + > + <div + style=" + font-family: Open Sans, Helvetica, Arial, + sans-serif; + font-size: 13px; + line-height: 22px; + text-align: left; + color: #797e82; + " + > + <p style="margin: 10px 0; text-align: center"> + You have entered the email address <b>%{email}</b> in + Vespa Cloud. + </p> + <p style="margin: 10px 0; text-align: center"> + Please verify this email address by clicking the + button below. + </p> + </div> + </td> + </tr> + <tr> + <td + align="center" + vertical-align="middle" + style=" + font-size: 0px; + padding: 10px 25px; + padding-top: 20px; + padding-bottom: 20px; + word-break: break-word; + " + > + <table + border="0" + cellpadding="0" + cellspacing="0" + role="presentation" + style="border-collapse: separate; line-height: 100%" + > + <tbody> + <tr> + <td + align="center" + bgcolor="#3B9FDE" + role="presentation" + style=" + border: none; + border-radius: 100px; + cursor: auto; + mso-padding-alt: 15px 25px 15px 25px; + background: #3b9fde; + " + valign="middle" + > + <a + href="https://%{consoleUrl}/verify?code=%{code}" + style=" + display: inline-block; + background: #3b9fde; + color: #ffffff; + font-family: Open Sans, Helvetica, Arial, + sans-serif; + font-size: 13px; + font-weight: normal; + line-height: 120%; + margin: 0; + text-decoration: none; + text-transform: none; + padding: 15px 25px 15px 25px; + mso-padding-alt: 0px; + border-radius: 100px; + " + target="_blank" + ><b style="font-weight: 700" + ><b style="font-weight: 700" + >Verify your email</b + ></b + ></a + > + </td> + </tr> + </tbody> + </table> + </td> + </tr> + <tr> + <td + align="left" + style=" + font-size: 0px; + padding: 0px 25px 0px 25px; + padding-top: 0px; + padding-right: 50px; + padding-bottom: 0px; + padding-left: 50px; + word-break: break-word; + " + > + <div + style=" + font-family: Open Sans, Helvetica, Arial, + sans-serif; + font-size: 13px; + line-height: 22px; + text-align: left; + color: #797e82; + " + > + <p style="margin: 10px 0; text-align: center"> + Or copy and paste this link into your browser + </p> + <p style="margin: 10px 0; text-align: center"> + <a + target="_blank" + rel="noopener noreferrer" + href="https://%{consoleUrl}/verify?code=%{code}" + style="color: #3b9fde" + >https://%{consoleUrl}/verify?code=%{code}</a + > + </p> + </div> + </td> + </tr> + </tbody> + </table> + </div> + <!--[if mso | IE]></td></tr></table><![endif]--> + </td> + </tr> + </tbody> + </table> + </div> + <!--[if mso | IE]></td></tr></table><![endif]--> + </div> + </body> +</html> diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java index 873ab435444..edea07e205c 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java @@ -12,9 +12,11 @@ import com.yahoo.vespa.hosted.controller.tenant.TenantContacts; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.net.URI; import java.time.Duration; import java.util.List; +import static com.yahoo.yolean.Exceptions.uncheck; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -27,7 +29,7 @@ class MailVerifierTest { private final ControllerTester tester = new ControllerTester(SystemName.Public); private final MockMailer mailer = tester.serviceRegistry().mailer(); - private final MailVerifier mailVerifier = new MailVerifier(tester.controller().tenants(), mailer, tester.curator(), tester.clock()); + private final MailVerifier mailVerifier = new MailVerifier(URI.create("https://dashboard.uri.example.com"), tester.controller().tenants(), mailer, tester.curator(), tester.clock()); private static final TenantName tenantName = TenantName.from("scoober"); private static final String mail = "unverified@bar.com"; @@ -53,9 +55,7 @@ class MailVerifierTest { mailVerifier.sendMailVerification(tenantName, mail, PendingMailVerification.MailType.NOTIFICATIONS); // Verify mail is sent - var expectedMail = "message"; assertEquals(1, mailer.inbox(mail).size()); - assertEquals(expectedMail, mailer.inbox(mail).get(0).message()); // Verify ZK data is updated var writtenMailVerification = tester.curator().listPendingMailVerifications().get(0); |