1use crate::router::RequestContext;
2
3#[derive(Debug, Clone)]
5pub struct SigV4Credentials {
6 pub access_key: String,
7 pub region: String,
8 pub service: String,
9 pub date: String,
10 pub signed_headers: Vec<String>,
11 pub signature: String,
12}
13
14pub fn parse_authorization(header: &str) -> Option<SigV4Credentials> {
19 let header = header.strip_prefix("AWS4-HMAC-SHA256")?.trim_start();
20
21 let mut credential = None;
22 let mut signed_headers = None;
23 let mut signature = None;
24
25 for part in header.split(',') {
27 let part = part.trim();
28 if let Some(val) = part.strip_prefix("Credential=") {
29 credential = Some(val.trim());
30 } else if let Some(val) = part.strip_prefix("SignedHeaders=") {
31 signed_headers = Some(val.trim());
32 } else if let Some(val) = part.strip_prefix("Signature=") {
33 signature = Some(val.trim());
34 }
35 }
36
37 let credential = credential?;
38 let parts: Vec<&str> = credential.split('/').collect();
39 if parts.len() < 5 {
40 return None;
41 }
42
43 Some(SigV4Credentials {
44 access_key: parts[0].to_string(),
45 date: parts[1].to_string(),
46 region: parts[2].to_string(),
47 service: parts[3].to_string(),
48 signed_headers: signed_headers
49 .unwrap_or("")
50 .split(';')
51 .map(|s| s.to_string())
52 .collect(),
53 signature: signature.unwrap_or("").to_string(),
54 })
55}
56
57pub fn build_request_context(
59 creds: &SigV4Credentials,
60 method: &str,
61 uri: &str,
62 default_account_id: &str,
63) -> RequestContext {
64 let mut ctx = RequestContext::new(&creds.service, &creds.region);
65 ctx.account_id = default_account_id.to_string();
66 ctx.access_key = Some(creds.access_key.clone());
67 ctx.method = method.to_string();
68 ctx.uri = uri.to_string();
69 ctx
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn test_parse_sigv4_authorization() {
78 let header = "AWS4-HMAC-SHA256 \
79 Credential=AKIAIOSFODNN7EXAMPLE/20230101/us-east-1/s3/aws4_request, \
80 SignedHeaders=host;x-amz-date, \
81 Signature=abcdef1234567890";
82
83 let creds = parse_authorization(header).unwrap();
84 assert_eq!(creds.access_key, "AKIAIOSFODNN7EXAMPLE");
85 assert_eq!(creds.date, "20230101");
86 assert_eq!(creds.region, "us-east-1");
87 assert_eq!(creds.service, "s3");
88 assert_eq!(creds.signed_headers, vec!["host", "x-amz-date"]);
89 assert_eq!(creds.signature, "abcdef1234567890");
90 }
91
92 #[test]
93 fn test_parse_invalid_header() {
94 assert!(parse_authorization("Bearer token123").is_none());
95 assert!(parse_authorization("").is_none());
96 }
97
98 #[test]
99 fn test_build_request_context() {
100 let creds = SigV4Credentials {
101 access_key: "AKID".to_string(),
102 region: "eu-west-1".to_string(),
103 service: "dynamodb".to_string(),
104 date: "20230101".to_string(),
105 signed_headers: vec!["host".to_string()],
106 signature: "sig".to_string(),
107 };
108
109 let ctx = build_request_context(&creds, "POST", "/", "123456789012");
110 assert_eq!(ctx.account_id, "123456789012");
111 assert_eq!(ctx.region, "eu-west-1");
112 assert_eq!(ctx.service, "dynamodb");
113 assert_eq!(ctx.access_key.unwrap(), "AKID");
114 }
115}