use super::User;
use super::user_info::{UserBuilder, UserInfo};
#[allow(deprecated)]
use aes::{
Aes128,
cipher::{BlockDecryptMut, KeyIvInit, block_padding::Pkcs7, generic_array::GenericArray},
};
use base64::{Engine, engine::general_purpose::STANDARD};
use cbc::Decryptor;
use hex::encode;
use hmac::{Hmac, Mac};
use http::Method;
use serde::{Deserialize, Serialize};
use serde_json::from_slice;
use sha2::Sha256;
use tracing::{debug, instrument};
use crate::utils::{RequestBuilder, ResponseExt};
use crate::{Result, constants};
type Aes128CbcDec = Decryptor<Aes128>;
#[derive(Serialize, Deserialize, Clone)]
pub struct Credential {
#[serde(rename = "openid")]
open_id: String,
session_key: String,
#[serde(skip_serializing_if = "Option::is_none")]
union_id: Option<String>,
}
impl Credential {
pub fn open_id(&self) -> &str {
&self.open_id
}
pub fn session_key(&self) -> &str {
&self.session_key
}
pub fn union_id(&self) -> Option<&str> {
self.union_id.as_deref()
}
#[instrument(skip(self, encrypted_data, iv))]
pub fn decrypt(&self, encrypted_data: &str, iv: &str) -> Result<UserInfo> {
debug!("encrypted_data: {}", encrypted_data);
debug!("iv: {}", iv);
let key = STANDARD.decode(self.session_key.as_bytes())?;
let iv = STANDARD.decode(iv.as_bytes())?;
#[allow(deprecated)]
let decryptor = Aes128CbcDec::new(
&GenericArray::clone_from_slice(&key),
&GenericArray::clone_from_slice(&iv),
);
let encrypted_data = STANDARD.decode(encrypted_data.as_bytes())?;
let buffer = decryptor.decrypt_padded_vec_mut::<Pkcs7>(&encrypted_data)?;
let builder = from_slice::<UserBuilder>(&buffer)?;
debug!("user builder: {:#?}", builder);
Ok(builder.build())
}
}
impl std::fmt::Debug for Credential {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Credential")
.field("open_id", &self.open_id)
.field("session_key", &"********")
.field("union_id", &self.union_id)
.finish()
}
}
type HmacSha256 = Hmac<Sha256>;
impl User {
#[instrument(skip(self, session_key, open_id))]
pub async fn check_session_key(&self, session_key: &str, open_id: &str) -> Result<()> {
let mut mac = HmacSha256::new_from_slice(session_key.as_bytes())?;
mac.update(b"");
let hasher = mac.finalize();
let signature = encode(hasher.into_bytes());
let query = serde_json::json!({
"openid": open_id.to_string(),
"signature":signature,
"sig_method": "hmac_sha256".to_string()
});
let request = RequestBuilder::new(constants::CHECK_SESSION_KEY_END_POINT)
.query(query)
.method(Method::GET)
.build()?;
let client = &self.client.client;
let response = client.execute(request).await?;
debug!("response: {:#?}", response);
response.to_json::<()>()
}
#[instrument(skip(self, open_id))]
pub async fn reset_session_key(&self, session_key: &str, open_id: &str) -> Result<Credential> {
let mut mac = HmacSha256::new_from_slice(session_key.as_bytes())?;
mac.update(b"");
let hasher = mac.finalize();
let signature = encode(hasher.into_bytes());
let query = serde_json::json!({
"access_token":self.client.token().await?,
"openid": open_id.to_string(),
"signature":signature,
"sig_method": "hmac_sha256".to_string()
});
let request = RequestBuilder::new(constants::RESET_SESSION_KEY_END_POINT)
.query(query)
.method(Method::GET)
.build()?;
let client = &self.client.client;
let response = client.execute(request).await?;
debug!("response: {:#?}", &response);
response.to_json::<Credential>()
}
}