sns: Save messages to disk

Upon receipt of a notification or unsubscribe confirmation message from
SNS, after the message signature has been verified, the receiver will
now write the re-serialized contents of the message out to the
filesystem.  This will allow the messages to be inspected later in order
to develop additional functionality for this service.

The messages are saved in a `messages` director within the current
working directory.  This directory contains a subdirectory for each SNS
topic.  Within the topic subdirectories, the each message is saved in a
file named with the message timestamp and ID.
master
Dustin 2022-09-05 09:45:44 -05:00
parent ab45823654
commit ac1b20d910
3 changed files with 58 additions and 4 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
/messages
/target

View File

@ -4,7 +4,7 @@
//! the AWS Documentation.
//!
//! [0]: https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html
use serde::Deserialize;
use serde::{Deserialize, Serialize};
/// Union for all known SNS message types
///
@ -24,7 +24,7 @@ pub enum Message {
/// See also: [HTTP/HTTPS subscription confirmation JSON format][0]
///
/// [0]: https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html#http-subscription-confirmation-json
#[derive(Deserialize)]
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct SubscriptionConfirmationMessage {
#[serde(rename = "MessageId")]
@ -46,7 +46,7 @@ pub struct SubscriptionConfirmationMessage {
/// See also: [HTTP/HTTPS notification JSON format][0]
///
/// [0]: https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html#http-notification-json
#[derive(Deserialize)]
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct NotificationMessage {
#[serde(rename = "MessageId")]
@ -67,7 +67,7 @@ pub struct NotificationMessage {
/// See also: [HTTP/HTTPS unsubscribe confirmation JSON format][0]
///
/// [0]: https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html#http-unsubscribe-confirmation-json
#[derive(Deserialize)]
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct UnsubscribeConfirmationMessage {
#[serde(rename = "MessageId")]

View File

@ -2,8 +2,11 @@
pub mod error;
pub mod sig;
use std::path::PathBuf;
use log::{debug, error, info};
use reqwest::Url;
use serde::Serialize;
use crate::model::sns::*;
use error::SnsError;
@ -29,6 +32,7 @@ pub async fn handle_unsubscribe(
msg: UnsubscribeConfirmationMessage,
) -> Result<(), SnsError> {
verify(&msg, &msg.signing_cert_url).await?;
save_message(&msg.topic_arn, &msg.timestamp, &msg.message_id, &msg);
Ok(())
}
@ -38,6 +42,7 @@ pub async fn handle_unsubscribe(
/// a file for later inspection.
pub async fn handle_notify(msg: NotificationMessage) -> Result<(), SnsError> {
verify(&msg, &msg.signing_cert_url).await?;
save_message(&msg.topic_arn, &msg.timestamp, &msg.message_id, &msg);
Ok(())
}
@ -141,3 +146,51 @@ async fn confirm_subscription(msg: &SubscriptionConfirmationMessage) {
}
}
}
fn save_message<T: Serialize>(
topic: &str,
timestamp: &str,
msgid: &str,
msg: &T,
) {
let mut path = PathBuf::from("messages");
path.push(topic);
if !path.is_dir() {
if let Err(e) = std::fs::create_dir_all(&path) {
error!("Could not create message storage directory: {}", e);
return;
}
}
path.push(format!("{}.{}", timestamp, msgid));
match serde_json::to_string(msg) {
Ok(data) => match std::fs::write(&path, &data) {
Ok(_) => info!(
"Wrote message {} to {}",
&msgid,
&path.to_string_lossy()
),
Err(e) => error!("Could not save message: {}", e),
},
Err(e) => error!("Failed to serialize message: {}", e),
};
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_save_message() {
let msg = NotificationMessage {
message_id: "f0bc6e06-24d1-471d-a828-8411c9873996".into(),
topic_arn: "arn:aws:sns:us-east-2-566967686773:dchtest1".into(),
message: "Hello, world!".into(),
subject: None,
timestamp: "2022-09-05T214:29:34.232Z".into(),
signature_version: "0".into(),
signature: "".into(),
signing_cert_url: "".into(),
};
save_message(&msg.topic_arn, &msg.timestamp, &msg.message_id, &msg);
}
}