use std::io::Read; use std::str::FromStr; use crate::data_structures; fn deserialize_dmarc_report( reader: R, ) -> Result { serde_xml_rs::from_reader(reader) } impl From for data_structures::DMARCAlignment { fn from(item: String) -> Self { match item.as_str() { "r" => data_structures::DMARCAlignment::Relaxed(item), "s" => data_structures::DMARCAlignment::Strict(item), _ => { panic!("DMARC alignment value '{:?}' is not allowed", item); } } } } impl From for data_structures::DMARCPolicy { fn from(item: String) -> Self { match item.as_str() { "none" => data_structures::DMARCPolicy::None(item), "quarantine" => data_structures::DMARCPolicy::Quarantine(item), "reject" => data_structures::DMARCPolicy::Reject(item), _ => { panic!("DMARC policy value '{:?}' is not allowed", item); } } } } impl From for data_structures::DMARCFailureOption { fn from(item: String) -> Self { match item.as_str() { "0" => data_structures::DMARCFailureOption::AlignedPassFailure(item), "1" => data_structures::DMARCFailureOption::OtherAlignedPassFailure(item), "2" => data_structures::DMARCFailureOption::SignatureAlignmentFailure(item), "3" => data_structures::DMARCFailureOption::SPFFailure(item), _ => { panic!("DMARC failure option '{:?}' is not allowed", item); } } } } impl data_structures::DMARCReport { pub fn from_raw_report(report: data_structures::Feedback) -> data_structures::DMARCReport { data_structures::DMARCReport { id: report.report_metadata.report_id, org: report.report_metadata.org_name, record: data_structures::DMARCRecord { domain: report.policy_published.domain, adkim: data_structures::DMARCAlignment::from(report.policy_published.adkim), aspf: data_structures::DMARCAlignment::from(report.policy_published.aspf), policy: data_structures::DMARCPolicy::from(report.policy_published.p), subdomain_policy: data_structures::DMARCPolicy::from(report.policy_published.sp), percentage: match u64::from_str(&report.policy_published.pct) { Ok(pct) => pct, Err(_) => { panic!( "Percentage value '{:?}' for DMARC record is not valid", report.policy_published.pct ); } }, failure_opt: data_structures::DMARCFailureOption::from(report.policy_published.fo), }, dkim_pass: match report.record.auth_results.dkim.result.as_str() { "pass" => true, _ => false, }, spf_pass: match report.record.auth_results.spf.result.as_str() { "pass" => true, _ => false, }, } } } #[cfg(test)] mod test { use super::*; use std::fs::File; use std::io::BufReader; #[test] fn test_deserialize_dmarc_report() { let dmarc_report_file = File::open("fixtures/dmarc_report_fixture.xml").unwrap(); let reader = BufReader::new(dmarc_report_file); let expected_version = "1.0".to_string(); let expected_report_id = "3374fb5148ba40c1a5cf8e3d36f34a34".to_string(); let expected_begin_date = "1707436800".to_string(); let expected_policy_published_domain = "boitalett.re".to_string(); let expected_record_row_source_ip = "195.154.102.43".to_string(); let expected_record_row_policy_evaluated_dkim = "pass".to_string(); let expected_identifiers_envelope_to = "babilou.com".to_string(); let expected_auth_results_dkim_selector = "2023101501".to_string(); let expected_auth_results_spf_scope = "mfrom".to_string(); match deserialize_dmarc_report(reader) { Ok(report) => { assert_eq!(report.version, expected_version); assert_eq!(report.report_metadata.report_id, expected_report_id); assert_eq!(report.report_metadata.date_range.begin, expected_begin_date); assert_eq!( report.policy_published.domain, expected_policy_published_domain ); assert_eq!(report.record.row.source_ip, expected_record_row_source_ip); assert_eq!( report.record.row.policy_evaluated.dkim, expected_record_row_policy_evaluated_dkim ); assert_eq!( report.record.identifiers.envelope_to, expected_identifiers_envelope_to ); assert_eq!( report.record.auth_results.dkim.selector, expected_auth_results_dkim_selector ); assert_eq!( report.record.auth_results.spf.scope, expected_auth_results_spf_scope ); } Err(err) => { panic!("{:?}", err); } } } #[test] fn test_parse_dmarc_report() { let dmarc_report_file = File::open("fixtures/dmarc_report_fixture.xml").unwrap(); let reader = BufReader::new(dmarc_report_file); let raw_report = deserialize_dmarc_report(reader).unwrap(); let expected_dmarc_report_id = "3374fb5148ba40c1a5cf8e3d36f34a34".to_string(); let dmarc_report = data_structures::DMARCReport::from_raw_report(raw_report); assert_eq!(dmarc_report.id, expected_dmarc_report_id); } }