authz-sdk-rust 0.2.2

sdk that client's authentication and authorization
Documentation
use std::{collections::HashMap, str::FromStr};

use chrono::Utc;
use jsonwebtoken::{crypto::sign, Algorithm, EncodingKey};
use reqwest::{
    header::{HeaderName, HeaderValue},
    Method, Request, Url,
};
use serde::{Deserialize, Serialize};

use crate::{Error, Result};

#[derive(Debug, Deserialize, Serialize)]
pub struct Signature {
    pub access_key: String,
    pub algorithm: String,
    pub request_time: String,
    pub sign: String,
    pub signed_headers: String,
}

#[derive(Debug, Serialize, Ord, Eq, PartialOrd, PartialEq)]
struct SignHeader {
    pub key: String,
    pub value: String,
}

pub fn signature(
    value: &mut Signature,
    sign_headers: HashMap<String, String>,
    secret_access_key: String,
    seconds_offset: u64,
) -> Result<String> {
    // 校验时间
    let request_time = value.request_time.parse::<i64>().map_err(Error::any)?;
    let now = Utc::now().timestamp();
    if now.abs_diff(request_time).gt(&seconds_offset) {
        return Err(Error::Invalid("time span exceeds threshold".to_string()));
    }
    // 解析算法
    let algorithm = Algorithm::from_str(&value.algorithm).map_err(Error::any)?;

    // sign 签名生成
    value.sign = Default::default();
    let message = serde_json::to_string(&value).map_err(Error::any)?;
    // 校验头排序
    let mut sort_headers = Vec::new();
    for h in value
        .signed_headers
        .split(';')
        .collect::<Vec<&str>>()
        .iter()
    {
        match sign_headers.get_key_value(*h) {
            Some((k, v)) => sort_headers.push(SignHeader {
                key: k.to_owned(),
                value: v.to_owned(),
            }),
            None => return Err(Error::Invalid(format!("lack {}", *h))),
        }
    }
    sort_headers.sort();
    let signature = serde_json::to_string(&sort_headers).map_err(Error::any)?;
    let sign_result = sign(
        [message, signature].join(".").as_bytes(),
        &EncodingKey::from_secret(secret_access_key.as_bytes()),
        algorithm,
    )
    .map_err(Error::any)?;
    Ok(sign_result)
}

pub fn query(
    value: &mut Signature,
    sign_headers: HashMap<String, String>,
    secret_access_key: String,
    seconds_offset: u64,
) -> Result<String> {
    value.sign = signature(
        value,
        sign_headers.clone(),
        secret_access_key,
        seconds_offset,
    )?;
    let prefix = serde_urlencoded::to_string(value).map_err(Error::any)?;
    let suffix = serde_urlencoded::to_string(sign_headers).map_err(Error::any)?;
    Ok([prefix, suffix].join("&"))
}

pub fn request(
    method: Method,
    url: Url,
    value: &mut Signature,
    sign_headers: HashMap<String, String>,
    secret_access_key: String,
) -> Result<Request> {
    // default 15s
    value.sign = signature(value, sign_headers.clone(), secret_access_key, 15)?;
    let mut req = Request::new(method, url);

    req.headers_mut().append(
        HeaderName::from_static("access_key"),
        HeaderValue::from_str(&value.access_key).map_err(Error::any)?,
    );
    req.headers_mut().append(
        HeaderName::from_static("algorithm"),
        HeaderValue::from_str(&value.algorithm).map_err(Error::any)?,
    );
    req.headers_mut().append(
        HeaderName::from_static("request_time"),
        HeaderValue::from_str(&value.request_time).map_err(Error::any)?,
    );
    req.headers_mut().append(
        HeaderName::from_static("sign"),
        HeaderValue::from_str(&value.sign).map_err(Error::any)?,
    );
    req.headers_mut().append(
        HeaderName::from_static("signed_headers"),
        HeaderValue::from_str(&value.signed_headers).map_err(Error::any)?,
    );
    for (k, v) in sign_headers.iter() {
        req.headers_mut().append(
            HeaderName::from_str(k).map_err(Error::any)?,
            HeaderValue::from_str(v.as_str()).map_err(Error::any)?,
        );
    }
    Ok(req)
}