1use crate::error::Error::CommonError;
2use crate::Result;
3use base64ct::Encoding;
4use md5::Md5;
5use rsa::pkcs1::DecodeRsaPublicKey;
6use rsa::pkcs1v15::{Signature, VerifyingKey};
7use rsa::pkcs8::DecodePublicKey;
8use rsa::signature::Verifier;
9use rsa::{pss, Pkcs1v15Sign};
10use serde::{Deserialize, Serialize, Serializer};
11use serde_json::Value;
12use sha2::Sha256;
13use std::collections::HashMap;
14use crate::util::date::acs_short_date;
15
16#[derive(Debug, Serialize, Deserialize)]
17pub struct CallbackRequest {
18 #[serde(rename = "callbackUrl")]
19 pub url: String,
20 #[serde(rename = "callbackHost", skip_serializing_if = "Option::is_none")]
21 pub host: Option<String>,
22 #[serde(rename = "callbackBody")]
23 pub body: HashMap<String, BodyField>,
24 #[serde(rename = "callbackSNI")]
25 pub sni: bool,
26 #[serde(rename = "callbackBodyType")]
27 pub body_type: BodyType,
28}
29
30#[derive(Debug, Serialize, Deserialize)]
31pub struct Callback {
32 pub callback: String,
33 pub callback_var: String,
34 pub date: String,
35 pub content_type: String,
36}
37
38impl CallbackRequest {
39 pub fn new(url: &str, body: HashMap<String, BodyField>, body_type: BodyType) -> Self {
40 CallbackRequest {
41 url: url.to_string(),
42 body,
43 body_type,
44 sni: false,
45 host: None,
46 }
47 }
48}
49
50pub fn build_callback(
51 url: &str,
52 fields: Option<HashMap<String, BodyField>>,
53 custom_fields: Option<HashMap<String, Value>>,
54 body_type: BodyType,
55) -> Result<Callback> {
56 let mut body = if let Some(fields) = fields {
57 fields
58 } else {
59 HashMap::new()
60 };
61
62 let mut vars = HashMap::new();
63
64 if let Some(custom_fields) = custom_fields {
65 custom_fields.into_iter().for_each(|(k, v)| {
66 body.insert(k.clone(), BodyField::Var(k.clone()));
67 vars.insert(BodyField::Var(k), v);
68 });
69 }
70
71 let request = CallbackRequest::new(url, body, body_type);
72 let callback = serde_json::to_string(&request)?;
73 log::debug!("callback: {}", callback);
74 let callback = base64ct::Base64::encode_string(callback.as_bytes());
75
76 let callback_var = serde_json::to_string(&vars)?;
77 let callback_var = base64ct::Base64::encode_string(callback_var.as_bytes());
78 log::debug!("callback: {}", callback_var);
79 Ok(Callback {
80 callback,
81 callback_var,
82 date: acs_short_date(),
83 content_type: "application/octet-stream; charset=utf-8".to_string(),
84 })
85}
86
87const ALIYUN_PUBLIC_PEM: &str = r#"-----BEGIN PUBLIC KEY-----
88MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKs/JBGzwUB2aVht4crBx3oIPBLNsjGs
89C0fTXv+nvlmklvkcolvpvXLTjaxUHR3W9LXxQ2EHXAJfCB+6H2YF1k8CAwEAAQ==
90-----END PUBLIC KEY-----"#;
91
92pub fn verify(
93 authorization: &str,
94 path: &str,
95 queries: Option<String>,
96 body: Option<String>,
97) -> Result<()> {
98 let signed_str = format!(
99 "{}{}\n{}",
100 urlencoding::decode(path)?,
101 queries.unwrap_or_default(),
102 body.unwrap_or_default()
103 );
104 let sig = base64ct::Base64::decode_vec(authorization)?;
105 let key = rsa::RsaPublicKey::from_public_key_pem(ALIYUN_PUBLIC_PEM)
106 .map_err(|err| CommonError(err.to_string()))?;
107 let verifying_key: VerifyingKey<Md5> = VerifyingKey::<Md5>::new_unprefixed(key);
108 let signature = Signature::try_from(&sig[..]).map_err(|err| CommonError(err.to_string()))?;
109 Ok(verifying_key
110 .verify(signed_str.as_bytes(), &signature)
111 .map_err(|err| CommonError(err.to_string()))?)
112}
113
114#[derive(Debug, Serialize, Deserialize, Default)]
115pub enum BodyType {
116 #[default]
117 #[serde(rename = "application/x-www-form-urlencoded")]
118 Form,
119 #[serde(rename = "application/json")]
120 Body,
121}
122#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
123pub enum BodyField {
124 #[serde(rename = "${bucket}")]
125 Bucket,
126 #[serde(rename = "${object}")]
127 Object,
128 #[serde(rename = "${etag}")]
129 Etag,
130 #[serde(rename = "${size}")]
131 Size,
132 #[serde(rename = "${mimeType}")]
133 MimeType,
134 #[serde(rename = "${imageInfo.height}")]
135 ImageHeight,
136 #[serde(rename = "${imageInfo.width}")]
137 ImageWidth,
138 #[serde(rename = "${imageInfo.format}")]
139 ImageFormat,
140 #[serde(rename = "${crc64}")]
141 Crc64,
142 #[serde(rename = "${contentMd5}")]
143 Md5,
144 #[serde(rename = "${vpcId}")]
145 VpcId,
146 #[serde(rename = "${clientIp}")]
147 ClientIp,
148 #[serde(rename = "${reqId}")]
149 ReqId,
150 #[serde(rename = "${operation}")]
151 Operation,
152 #[serde(untagged, with = "body_value_ser")]
153 Var(String),
154}
155
156mod body_value_ser {
157 use serde::{de, Deserialize, Deserializer, Serializer};
158
159 pub fn serialize<S>(var: &String, serializer: S) -> Result<S::Ok, S::Error>
160 where
161 S: Serializer,
162 {
163 let s = format!("${{x:{}}}", var);
164 serializer.serialize_str(&s)
165 }
166
167 pub fn deserialize<'de, D>(deserializer: D) -> Result<String, D::Error>
168 where
169 D: Deserializer<'de>,
170 {
171 let s = String::deserialize(deserializer)?;
172 if s.starts_with("${x:") && s.ends_with('}') {
173 Ok(s[4..s.len() - 1].to_string())
174 } else {
175 Err(de::Error::custom(format!(
176 "Invalid format for Var, expected '${{x:...}}', got '{}'",
177 s
178 )))
179 }
180 }
181}
182
183mod test {
184 use crate::oss::callback::BodyField;
185 use serde::Serialize;
186 use serde_json::json;
187 use std::collections::HashMap;
188
189 #[derive(Serialize)]
190 struct V {
191 v: BodyField,
192 }
193
194 #[test]
195 fn test_callback() {
196 let ret = serde_json::to_string(&BodyField::Var("vpcId".to_string()));
197 assert_eq!(ret.unwrap(), "\"${x:vpcId}\"");
198
199 let ret = serde_json::to_string(&BodyField::VpcId);
200 assert_eq!(ret.unwrap(), "\"${vpcId}\"")
201 }
202
203 #[test]
204 fn test_build_callback() {
205 let ret = super::build_callback(
206 "http://127.0.0.1:8080/callback",
207 Some(HashMap::from([
208 ("bucket".to_string(), BodyField::Bucket),
209 ("object".to_string(), BodyField::Object),
210 ("etag".to_string(), BodyField::Etag),
211 ("size".to_string(), BodyField::Size),
212 ("mimeType".to_string(), BodyField::MimeType),
213 ("imageInfo.height".to_string(), BodyField::ImageHeight),
214 ])),
215 Some(HashMap::from([
216 ("abc".to_string(), json!("vpcId".to_string())),
217 ("ffd".to_string(), json!("clientIp")),
218 ("efc".to_string(), json!("reqId")),
219 ("asf".to_string(), json!("operation")),
220 ])),
221 super::BodyType::Form,
222 )
223 .unwrap();
224
225 println!("{:?}", ret)
226 }
227}