@@ -27,69 +27,15 @@ use rusoto_core::Region;
27
27
use rusoto_ses:: {
28
28
Body , Content , Destination , Message , SendEmailRequest , Ses , SesClient ,
29
29
} ;
30
- use serde:: { Deserialize , Serialize } ;
31
- // use serde_json::Value;
30
+ use serde:: Serialize ;
31
+ use serde_json:: Value ;
32
32
use simple_logger:: SimpleLogger ;
33
33
use std:: collections:: HashMap ;
34
34
use std:: fmt:: Debug ;
35
35
use std:: { fmt, process} ;
36
36
use tracing:: log:: LevelFilter ;
37
37
use tracing:: { error, info, warn} ;
38
38
39
- // LambdaRequest: Represents the incoming Request from AWS Lambda
40
- // This is deserialized into a struct payload
41
- //
42
- #[ derive( Debug , Default , Deserialize ) ]
43
- #[ serde( default ) ]
44
- pub struct LambdaRequest {
45
- /** lambda request body */
46
- records : Vec < LambdaRequestRecord > ,
47
- }
48
-
49
- #[ derive( Debug , Default , Deserialize ) ]
50
- #[ serde( default ) ]
51
- struct LambdaRequestRecord {
52
- /** event source */
53
- event_source : String ,
54
-
55
- /** event version */
56
- event_version : String ,
57
-
58
- /** event subscription arn*/
59
- event_subscription_arn : String ,
60
-
61
- /** SNS Message body */
62
- sns : SNSMessageBody ,
63
- }
64
-
65
- #[ derive( Debug , Default , Deserialize ) ]
66
- #[ serde( default , rename_all = "camelCase" ) ]
67
- struct SNSMessageBody {
68
- r#type : String ,
69
-
70
- message_id : String ,
71
-
72
- topic_arn : String ,
73
-
74
- subject : String ,
75
-
76
- /** SES Message request */
77
- message : SesMessageRequest ,
78
-
79
- timestamp : String ,
80
-
81
- signature_version : u32 ,
82
-
83
- signature : String ,
84
-
85
- signing_cert_url : String ,
86
-
87
- unsubscribe_url : String ,
88
-
89
- #[ serde( default , skip_serializing_if = "HashMap::is_empty" ) ]
90
- message_attributes : HashMap < String , String > ,
91
- }
92
-
93
39
/// LambdaResponse: The Outgoing response being passed by the Lambda
94
40
#[ derive( Debug , Default , Clone , Serialize ) ]
95
41
#[ serde( default , rename_all = "camelCase" ) ]
@@ -135,109 +81,10 @@ impl fmt::Display for LambdaResponse {
135
81
}
136
82
}
137
83
138
- #[ derive( Debug , Default , Deserialize ) ]
139
- #[ serde( default , rename_all = "camelCase" ) ]
140
- struct MessageHeader {
141
- name : String ,
142
-
143
- value : String ,
144
- }
145
-
146
- #[ derive( Debug , Default , Deserialize ) ]
147
- #[ serde( default , rename_all = "camelCase" ) ]
148
- struct ReceiptStatus {
149
- status : String ,
150
- }
151
-
152
- #[ derive( Debug , Default , Deserialize ) ]
153
- #[ serde( default , rename_all = "camelCase" ) ]
154
- struct LambdaInfo {
155
- r#type : String ,
156
-
157
- topic_arn : String ,
158
-
159
- function_arn : String ,
160
-
161
- invocation_type : String ,
162
- }
163
-
164
- #[ derive( Debug , Default , Deserialize ) ]
165
- #[ serde( default , rename_all = "camelCase" ) ]
166
- struct ReceiptInfo {
167
- timestamp : String ,
168
-
169
- processing_time_millis : u32 ,
170
-
171
- recipients : Vec < String > ,
172
-
173
- spam_verdict : ReceiptStatus ,
174
-
175
- virus_verdict : ReceiptStatus ,
176
-
177
- spf_verdict : ReceiptStatus ,
178
- dkim_verdict : ReceiptStatus ,
179
- dmarc_verdict : ReceiptStatus ,
180
- action : LambdaInfo ,
181
- }
182
-
183
- #[ derive( Debug , Default , Deserialize ) ]
184
- #[ serde( default , rename_all = "camelCase" ) ]
185
- struct CommonHeaderReq {
186
- return_path : String ,
187
-
188
- from : Vec < String > ,
189
-
190
- date : String ,
191
-
192
- to : Vec < String > ,
193
-
194
- cc : Vec < String > ,
195
-
196
- bcc : Vec < String > ,
197
-
198
- message_id : String ,
199
-
200
- subject : String ,
201
- }
202
-
203
- #[ derive( Debug , Default , Deserialize ) ]
204
- #[ serde( default , rename_all = "camelCase" ) ]
205
- struct MailInfoReq {
206
- timestamp : String ,
207
-
208
- source : String ,
209
-
210
- message_id : String ,
211
-
212
- destination : Vec < String > ,
213
-
214
- headers_truncated : bool ,
215
-
216
- headers : Vec < MessageHeader > ,
217
- common_headers : CommonHeaderReq ,
218
- }
219
-
220
- /// SesMessageRequest: SES Message
221
- #[ derive( Debug , Default , Deserialize ) ]
222
- #[ serde( default , rename_all = "camelCase" ) ]
223
- pub struct SesMessageRequest {
224
- /** notification type */
225
- notification_type : String ,
226
-
227
- /** receipt metadata **/
228
- receipt : ReceiptInfo ,
229
-
230
- /** Email content */
231
- content : String ,
232
-
233
- /** Email metadata */
234
- mail : MailInfoReq ,
235
- }
236
-
237
84
/// PrivatEmail_Handler: processes incoming messages from SNS
238
85
/// and forwards to the appropriate recipient email
239
86
pub ( crate ) async fn privatemail_handler (
240
- event : LambdaRequest ,
87
+ event : Value ,
241
88
ctx : Context ,
242
89
) -> Result < LambdaResponse , Error > {
243
90
// Enable Cloudwatch error logging at runtime
@@ -251,53 +98,94 @@ pub(crate) async fn privatemail_handler(
251
98
let email_config = config:: PrivatEmailConfig :: new_from_env ( ) ;
252
99
253
100
// Fetch request payload
254
- let sns_payload: & LambdaRequestRecord = event. records . first ( ) . unwrap ( ) ;
101
+ let sns_payload = event[ "Records" ] [ 0 ] [ "Sns" ] . as_object ( ) . unwrap ( ) ;
255
102
info ! ( "Raw Email Info: {:?}" , sns_payload) ;
256
103
257
- let email_info = & sns_payload. sns . message ;
258
- info ! ( "Email Message: {:?}" , email_info) ;
259
- let new_email_info: & SesMessageRequest = email_info;
260
- info ! ( "Email NotificationType: {:#?}" , new_email_info) ;
261
-
262
104
// skip spam messages
263
- if new_email_info. receipt . spam_verdict . status == "FAIL"
264
- || new_email_info. receipt . virus_verdict . status == "FAIL"
105
+ if sns_payload[ "Message" ] [ "receipt" ] [ "spamVerdict" ] [ "status" ]
106
+ . as_str ( )
107
+ . unwrap ( )
108
+ == "FAIL"
109
+ || sns_payload[ "Message" ] [ "receipt" ] [ "virusVerdict" ] [ "status" ]
110
+ . as_str ( )
111
+ . unwrap ( )
112
+ == "FAIL"
265
113
{
266
114
warn ! ( "Message contains spam or virus, skipping!" ) ;
267
115
process:: exit ( 200 ) ;
268
116
// Ok(LambdaResponse(200, "message skipped"))
269
117
}
270
118
271
119
// Rewrite Email From header to contain sender's name with forwarder's email address
120
+ let raw_from = sns_payload[ "Message" ] [ "mail" ] [ "commonHeaders" ]
121
+ [ "returnPath" ]
122
+ . as_str ( )
123
+ . unwrap ( )
124
+ . to_string ( ) ;
125
+ let from: Vec < String > = vec ! [ raw_from] ;
126
+
127
+ let to_emails: Option < Vec < String > > =
128
+ Some ( vec ! [ email_config. to_email. to_string( ) ] ) ;
129
+
130
+ info ! (
131
+ "Email Subject: {:#?}" ,
132
+ sns_payload[ "Message" ] [ "mail" ] [ "commonHeaders" ] [ "subject" ]
133
+ . as_str( )
134
+ . unwrap( )
135
+ . to_string( )
136
+ ) ;
137
+ info ! ( "From Email: {:#?}" , from) ;
138
+ info ! ( "To Email: {:#?}" , to_emails) ;
139
+ info ! (
140
+ "Email content: {:#?}" ,
141
+ sns_payload[ "Message" ] [ "content" ] . as_str( ) . unwrap( ) . to_string( )
142
+ ) ;
143
+
272
144
let ses_email_message = SendEmailRequest {
273
145
configuration_set_name : None ,
274
146
destination : Destination {
275
- bcc_addresses : Some ( new_email_info . mail . common_headers . bcc . clone ( ) ) ,
276
- cc_addresses : Some ( new_email_info . mail . common_headers . cc . clone ( ) ) ,
277
- to_addresses : Some ( vec ! [ email_config . to_email . clone ( ) ] ) ,
147
+ bcc_addresses : Some ( vec ! [ "" . to_string ( ) ] ) ,
148
+ cc_addresses : Some ( vec ! [ "" . to_string ( ) ] ) ,
149
+ to_addresses : to_emails ,
278
150
} ,
279
151
message : Message {
280
152
body : Body {
281
153
html : Some ( Content {
282
154
charset : Some ( String :: from ( "utf-8" ) ) ,
283
- data : new_email_info. content . to_string ( ) ,
155
+ data : sns_payload[ "Message" ] [ "content" ]
156
+ . as_str ( )
157
+ . unwrap ( )
158
+ . to_string ( ) ,
284
159
} ) ,
285
160
text : Some ( Content {
286
161
charset : Some ( String :: from ( "utf-8" ) ) ,
287
- data : new_email_info. content . to_string ( ) ,
162
+ data : sns_payload[ "Message" ] [ "content" ]
163
+ . as_str ( )
164
+ . unwrap ( )
165
+ . to_string ( ) ,
288
166
} ) ,
289
167
} ,
290
168
subject : Content {
291
169
charset : Some ( String :: from ( "utf-8" ) ) ,
292
- data : new_email_info. mail . common_headers . subject . clone ( ) ,
170
+ data : sns_payload[ "Message" ] [ "mail" ] [ "commonHeaders" ]
171
+ [ "subject" ]
172
+ . as_str ( )
173
+ . unwrap ( )
174
+ . to_string ( ) ,
293
175
} ,
294
176
} ,
295
- reply_to_addresses : Some (
296
- new_email_info. mail . common_headers . from . clone ( ) ,
177
+ reply_to_addresses : Some ( from) ,
178
+ return_path : Some (
179
+ sns_payload[ "Message" ] [ "mail" ] [ "source" ]
180
+ . as_str ( )
181
+ . unwrap ( )
182
+ . to_string ( ) ,
297
183
) ,
298
- return_path : Some ( new_email_info. mail . source . to_string ( ) ) ,
299
184
return_path_arn : None ,
300
- source : new_email_info. mail . source . clone ( ) ,
185
+ source : sns_payload[ "Message" ] [ "mail" ] [ "source" ]
186
+ . as_str ( )
187
+ . unwrap ( )
188
+ . to_string ( ) ,
301
189
source_arn : None ,
302
190
tags : None ,
303
191
} ;
@@ -318,29 +206,29 @@ pub(crate) async fn privatemail_handler(
318
206
#[ cfg( test) ]
319
207
mod tests {
320
208
use super :: * ;
321
- use std:: fs:: File ;
322
- use std:: io:: BufReader ;
323
209
use std:: path:: PathBuf ;
210
+ use std:: { env, fs} ;
324
211
325
- fn read_test_event ( ) -> Result < LambdaRequest , Error > {
212
+ fn read_test_event ( ) -> Value {
326
213
// Open the file in read-only mode with buffer.
327
214
328
215
let mut srcdir = PathBuf :: from ( env ! ( "CARGO_MANIFEST_DIR" ) ) ;
329
216
srcdir. push ( "tests/payload/testEvent.json" ) ;
330
217
println ! ( "Cur Dir: {}" , srcdir. display( ) ) ;
331
- let file = File :: open ( srcdir) ?;
332
- let reader = BufReader :: new ( file) ;
333
218
334
- // Read the JSON contents of the file as an instance of `User`.
335
- let req = serde_json:: from_reader ( reader) ?;
219
+ // Read the JSON contents of the file as an instance of `String`.
220
+ let input_str = fs:: read_to_string ( srcdir. as_path ( ) ) . unwrap ( ) ;
221
+ info ! ( "Input str: {}" , input_str) ;
336
222
337
- // Return the `LambdaRequest `.
338
- Ok ( req )
223
+ // Return the `Value `.
224
+ return serde_json :: from_str ( input_str . as_str ( ) ) . unwrap ( ) ;
339
225
}
340
226
341
227
#[ tokio:: test]
342
228
// #[ignore]
343
229
async fn handler_handles ( ) {
230
+ env
:: set_var ( "TO_EMAIL" , "[email protected] " ) ;
231
+ env
:: set_var ( "FROM_EMAIL" , "[email protected] " ) ;
344
232
let test_event = read_test_event ( ) ;
345
233
assert_eq ! (
346
234
privatemail_handler( test_event, Context :: default ( ) )
0 commit comments