use std::convert::Infallible;
use axum::extract::FromRequestParts;
use axum::http::request::Parts;
use super::Guarded;
use crate::guard::Guard;
pub type GuardExtractor = Guard;
impl<S> FromRequestParts<S> for Guard
where
S: Guarded + Send + Sync,
{
type Rejection = Infallible;
async fn from_request_parts(_parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
Ok(state.guard().clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
use axum::Router;
use axum::body::Body;
use axum::extract::Extension;
use axum::http::{Request, StatusCode};
use axum::routing::get;
use tower::ServiceExt as _;
fn guard() -> Guard {
Guard::from_master_key(&[7u8; 32], "aes-256-gcm").unwrap()
}
#[derive(Clone)]
struct BareState {
encryption: Guard,
}
impl Guarded for BareState {
fn guard(&self) -> &Guard {
&self.encryption
}
}
#[derive(Clone)]
struct AppState {
#[allow(dead_code)]
pool: std::sync::Arc<String>,
encryption: Guard,
}
impl Guarded for AppState {
fn guard(&self) -> &Guard {
&self.encryption
}
}
#[tokio::test]
async fn extracts_directly_from_parts() {
let state = AppState {
pool: std::sync::Arc::new("pool".into()),
encryption: guard(),
};
let (mut parts, _) = Request::new(()).into_parts();
let extracted = Guard::from_request_parts(&mut parts, &state).await.unwrap();
assert_eq!(extracted.identifiers().len(), 5);
}
#[tokio::test]
async fn handler_can_take_a_guard_argument() {
async fn handler(guard: Guard) -> String {
let blob = guard.encrypt("13800138000".as_bytes()).unwrap();
String::from_utf8(guard.decrypt(&blob).unwrap()).unwrap()
}
let state = AppState {
pool: std::sync::Arc::new("pool".into()),
encryption: guard(),
};
let app = Router::new().route("/", get(handler)).with_state(state);
let response = app
.oneshot(Request::builder().uri("/").body(Body::empty()).unwrap())
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
.await
.unwrap();
assert_eq!(&body[..], b"13800138000");
}
#[tokio::test]
async fn handler_and_state_share_one_encrypter() {
async fn handler(guard: Guard, Extension(token): Extension<Vec<u8>>) -> String {
guard
.decrypt(&token)
.map(|plain| String::from_utf8_lossy(&plain).into_owned())
.unwrap_or_else(|_| "解不开".into())
}
let state = BareState {
encryption: guard(),
};
let token = state.encryption.encrypt("共享".as_bytes()).unwrap();
let app = Router::new()
.route("/", get(handler))
.layer(Extension(token))
.with_state(state);
let response = app
.oneshot(Request::builder().uri("/").body(Body::empty()).unwrap())
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
.await
.unwrap();
assert_eq!(&body[..], "共享".as_bytes());
}
}