/
SesHelper.java
231 lines (194 loc) · 10.6 KB
/
SesHelper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
package org.sagebionetworks.bridge.udd.helper;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClient;
import com.amazonaws.services.simpleemail.model.Body;
import com.amazonaws.services.simpleemail.model.Content;
import com.amazonaws.services.simpleemail.model.Destination;
import com.amazonaws.services.simpleemail.model.Message;
import com.amazonaws.services.simpleemail.model.RawMessage;
import com.amazonaws.services.simpleemail.model.SendEmailRequest;
import com.amazonaws.services.simpleemail.model.SendEmailResult;
import com.amazonaws.services.simpleemail.model.SendRawEmailRequest;
import com.amazonaws.services.simpleemail.model.SendRawEmailResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.sagebionetworks.bridge.udd.s3.PresignedUrlInfo;
import org.sagebionetworks.bridge.workerPlatform.bridge.AccountInfo;
import org.sagebionetworks.bridge.workerPlatform.dynamodb.AppInfo;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Properties;
/** Helper class to format and send the presigned URL as an email through SES. */
@Component
public class SesHelper {
private static final Logger LOG = LoggerFactory.getLogger(SesHelper.class);
// JIRA for templatizing emails https://sagebionetworks.jira.com/browse/BRIDGE-2274
private static final String SUBJECT_TEMPLATE = "Your requested data from %s";
private static final String BODY_TEMPLATE_HTML = "<html>%n" +
" <body>%n" +
" <p>Your <a href=\"%s\">requested data download</a> is now available.</p>%n" +
" <p>This link will expire on %s.</p>%n" +
" <p>Our system updates once per day. So, if you updated your information in the last 24 hours, it may not be available to download until tomorrow.</p>%n" +
" </body>%n" +
"</html>";
private static final String BODY_TEMPLATE_TEXT = "To download your requested data, please click on the following link:%n" +
"%s%n" +
"%n" +
"This link will expire on %s.%n" +
"%n" +
"Our system updates once per day. So, if you updated your information in the last 24 hours, it may not be available to download until tomorrow.";
private static final String NO_DATA_BODY_TEXT = "There was no data available for your request. Data will only be available if your sharing\n" +
"settings are set to share data. Please check your sharing settings and please wait at\n" +
"least 24 hours for data to finish processing.";
private static final String NO_DATA_BODY_HTML = "<html>\n" +
" <body>\n" +
" <p>\n" +
NO_DATA_BODY_TEXT +
" </p>\n" +
" </body>\n" +
"</html>";
private static final String ATTACHMENT_BODY_TEMPLATE_TEXT = "Your requested data is now available. " +
"To download your requested data, please click on this link (which expires after %s):%n%n%s";
private static final String ATTACHMENT_BODY_TEMPLATE_HTML = "<html>%n" +
" <body>%n" +
"<p>Your requested data is now available. To download your requested data, please click on " +
"this link (which expires after %s):</p><p><a href='%s'>%s</a></p>" +
" </body>%n" +
"</html>";
private static final String CHARSET_UTF_8 = "UTF-8";
private static final String CONTENT_SUBTYPE_ALTERNATIVE = "alternative";
private static final String CONTENT_SUBTYPE_MIXED= "mixed";
private static final String CONTENT_TYPE_TEXT_HTML = "text/html; charset=UTF-8";
private static final String CONTENT_TYPE_TEXT_PLAIN = "text/plain";
private AmazonSimpleEmailServiceClient sesClient;
/** SES client. */
@Autowired
public final void setSesClient(AmazonSimpleEmailServiceClient sesClient) {
this.sesClient = sesClient;
}
/**
* Sends a notice to the given account that no data could be found. This notice also gives basic instructions on
* how they could enable data.
*
* @param appInfo
* app info, used to construct the email message, must be non-null
* @param accountInfo
* account to send the notice to, must be non-null
*/
public void sendNoDataMessageToAccount(AppInfo appInfo, AccountInfo accountInfo) {
Body body = new Body().withHtml(new Content(NO_DATA_BODY_HTML)).withText(new Content(NO_DATA_BODY_TEXT));
sendEmailToAccount(appInfo, accountInfo, body);
}
/**
* Sends the presigned URL to the specified account. This also uses the app info to construct the email message.
*
* @param appInfo
* app info, used to construct the email message, must be non-null
* @param presignedUrlInfo
* presigned URL info (URL and expiration date), which should be sent to the specified account, must be
* non-null
* @param accountInfo
* account to send the presigned URL to, must be non-null
*/
public void sendPresignedUrlToAccount(AppInfo appInfo, PresignedUrlInfo presignedUrlInfo,
AccountInfo accountInfo) {
String presignedUrlStr = presignedUrlInfo.getUrl().toString();
String expirationTimeStr = presignedUrlInfo.getExpirationTime().toString();
String bodyHtmlStr = String.format(BODY_TEMPLATE_HTML, presignedUrlStr, expirationTimeStr);
String bodyTextStr = String.format(BODY_TEMPLATE_TEXT, presignedUrlStr, expirationTimeStr);
Body body = new Body().withHtml(new Content(bodyHtmlStr)).withText(new Content(bodyTextStr));
sendEmailToAccount(appInfo, accountInfo, body);
}
/**
* Sends the attachment to the specified account. This also uses the app info to construct the email message.
*
* @param appInfo
* app info, used to construct the email message, must be non-null
* @param accountInfo
* account to send the attachment to, must be non-null
* @param downloadURL
* pre-signed S3 download URL (good for a limited period of time)
* @param expirationDescription
* an English-language description of the period that the link will be valid (e.g. "24 hours")
*/
public void sendEmailWithTempDownloadLinkToAccount(AppInfo appInfo, AccountInfo accountInfo, String downloadURL, String expirationDescription) throws MessagingException, IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
MimeMessage message = makeRawEmailMessage(appInfo, accountInfo, downloadURL, expirationDescription);
message.writeTo(outputStream);
RawMessage rawMessage = new RawMessage(ByteBuffer.wrap(outputStream.toByteArray()));
SendRawEmailRequest rawEmailRequest = new SendRawEmailRequest(rawMessage);
SendRawEmailResult sendRawEmailResult = sesClient.sendRawEmail(rawEmailRequest);
LOG.info("Sent raw email to account " + accountInfo.getUserId() + " with SES message ID " + sendRawEmailResult.getMessageId());
}
/**
* Helper method, which sends the given email message body to the given account
*
* @param appInfo
* app info, used to construct the email message, must be non-null
* @param accountInfo
* account to send the email to, must be non-null
* @param body
* email message body
*/
private void sendEmailToAccount(AppInfo appInfo, AccountInfo accountInfo, Body body) {
// from address
String fromAddress = appInfo.getSupportEmail();
// to address
Destination destination = new Destination().withToAddresses(accountInfo.getEmailAddress());
// subject
String appName = appInfo.getName();
String subjectStr = String.format(SUBJECT_TEMPLATE, appName);
Content subject = new Content(subjectStr);
// send message
Message message = new Message(subject, body);
SendEmailRequest sendEmailRequest = new SendEmailRequest(fromAddress, destination, message);
SendEmailResult sendEmailResult = sesClient.sendEmail(sendEmailRequest);
LOG.info("Sent email to account " + accountInfo.getUserId() + " with SES message ID " +
sendEmailResult.getMessageId());
}
private MimeMessage makeRawEmailMessage(AppInfo appInfo, AccountInfo accountInfo, String downloadURL, String expiration) throws MessagingException {
// from address
String fromAddress = appInfo.getSupportEmail();
// to address
String destination = accountInfo.getEmailAddress();
// subject
String appName = appInfo.getName();
String subjectStr = String.format(SUBJECT_TEMPLATE, appName);
// create message
Session session = Session.getDefaultInstance(new Properties());
MimeMessage message = new MimeMessage(session);
// add subject, from address, recipient address
message.setSubject(subjectStr, CHARSET_UTF_8);
message.setFrom(new InternetAddress(fromAddress));
message.setRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(destination));
// create multipart/alternative child container, wrapper for the HTML and text parts
MimeMultipart messageBody = new MimeMultipart(CONTENT_SUBTYPE_ALTERNATIVE);
MimeBodyPart wrap = new MimeBodyPart();
MimeBodyPart textPart = new MimeBodyPart();
textPart.setContent(String.format(ATTACHMENT_BODY_TEMPLATE_TEXT, expiration, downloadURL),
CONTENT_TYPE_TEXT_PLAIN);
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(String.format(ATTACHMENT_BODY_TEMPLATE_HTML, expiration, downloadURL, downloadURL),
CONTENT_TYPE_TEXT_HTML);
// add text and HTML parts to child container
messageBody.addBodyPart(textPart);
messageBody.addBodyPart(htmlPart);
// add child container to wrapper obj
wrap.setContent(messageBody);
// create multipart/mixed parent container
MimeMultipart mimeMessage = new MimeMultipart(CONTENT_SUBTYPE_MIXED);
// add parent container to message
message.setContent(mimeMessage);
// add multipart/alternative part to message
mimeMessage.addBodyPart(wrap);
return message;
}
}