Skip to content

Commit

Permalink
Added endpoint to access individual reports
Browse files Browse the repository at this point in the history
  • Loading branch information
cry-inc committed Mar 17, 2024
1 parent 69534aa commit e7f8ddc
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 17 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ flate2 = "1"
futures = "0.3"
tracing = "0.1"
base64 = "0.22"
serde_json = "1"
mailparse = "0.14"
axum-server = "0.6"
serde-xml-rs = "0.6"
Expand Down
32 changes: 16 additions & 16 deletions src/dmarc_report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
use serde::{Deserialize, Serialize};
use std::net::IpAddr;

#[derive(Debug, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct DateRangeType {
pub begin: u64,
pub end: u64,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct ReportMetadataType {
pub org_name: String,
pub email: String,
Expand All @@ -22,15 +22,15 @@ pub struct ReportMetadataType {
pub error: Option<Vec<String>>,
}

#[derive(Debug, Deserialize, PartialEq)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub enum AlignmentType {
#[serde(rename = "r")]
Relaxed,
#[serde(rename = "s")]
Strict,
}

#[derive(Debug, Deserialize, PartialEq)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum DispositionType {
/// There is no preference on how a failed DMARC should be handled.
Expand All @@ -41,7 +41,7 @@ pub enum DispositionType {
Reject,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct PolicyPublishedType {
pub domain: String,
pub adkim: Option<AlignmentType>,
Expand All @@ -59,7 +59,7 @@ pub enum DmarcResultType {
Fail,
}

#[derive(Debug, Deserialize, PartialEq)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum PolicyOverrideType {
Forwarded,
Expand All @@ -70,29 +70,29 @@ pub enum PolicyOverrideType {
Other,
}

#[derive(Debug, Deserialize, PartialEq)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct PolicyOverrideReason {
#[serde(rename = "type")]
pub kind: PolicyOverrideType,
pub comment: Option<String>,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct PolicyEvaluatedType {
pub disposition: DispositionType,
pub dkim: Option<DmarcResultType>,
pub spf: Option<DmarcResultType>,
pub reason: Option<Vec<PolicyOverrideReason>>,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct RowType {
pub source_ip: IpAddr,
pub count: usize,
pub policy_evaluated: PolicyEvaluatedType,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct IdentifierType {
pub envelope_to: Option<String>,
pub envelope_from: Option<String>,
Expand All @@ -113,15 +113,15 @@ pub enum DkimResultType {
PermanentError,
}

#[derive(Debug, Deserialize, PartialEq)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct DkimAuthResultType {
pub domain: String,
pub selector: Option<String>,
pub result: DkimResultType,
pub human_result: Option<String>,
}

#[derive(Debug, Deserialize, PartialEq)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum SpfDomainScope {
Helo,
Expand All @@ -144,27 +144,27 @@ pub enum SpfResultType {
PermanentError,
}

#[derive(Debug, Deserialize, PartialEq)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct SpfAuthResultType {
pub domain: String,
pub scope: Option<SpfDomainScope>,
pub result: SpfResultType,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct AuthResultType {
pub dkim: Option<Vec<DkimAuthResultType>>,
pub spf: Vec<SpfAuthResultType>,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct RecordType {
pub row: RowType,
pub identifiers: IdentifierType,
pub auth_results: AuthResultType,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct Report {
pub version: Option<String>,
pub report_metadata: ReportMetadataType,
Expand Down
24 changes: 23 additions & 1 deletion src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::config::Configuration;
use crate::state::AppState;
use anyhow::{Context, Result};
use axum::body::Body;
use axum::extract::Request;
use axum::extract::{Path, Request};
use axum::http::header::{self, AUTHORIZATION, WWW_AUTHENTICATE};
use axum::http::StatusCode;
use axum::middleware::{self, Next};
Expand Down Expand Up @@ -31,6 +31,7 @@ pub async fn run_http_server(config: &Configuration, state: Arc<Mutex<AppState>>
.route("/chart.umd.js", get(chart_js))
.route("/summary", get(summary))
.route("/reports", get(reports))
.route("/reports/:id", get(report))
.route_layer(CompressionLayer::new())
.route_layer(middleware::from_fn_with_state(
config.clone(),
Expand Down Expand Up @@ -255,3 +256,24 @@ async fn reports(State(state): State<Arc<Mutex<AppState>>>) -> impl IntoResponse
.collect();
Json(reports)
}

async fn report(
State(state): State<Arc<Mutex<AppState>>>,
Path(id): Path<usize>,
) -> impl IntoResponse {
let lock = state.lock().expect("Failed to lock app state");
if let Some(report) = lock.reports.get(id) {
let report_json = serde_json::to_string(report).expect("Failed to serialize JSON");
(
StatusCode::OK,
[(header::CONTENT_TYPE, "application/json")],
report_json,
)
} else {
(
StatusCode::NOT_FOUND,
[(header::CONTENT_TYPE, "text/plain")],
format!("Cannot find report with ID {id}"),
)
}
}

0 comments on commit e7f8ddc

Please sign in to comment.