use std::{
collections::HashMap,
fs::{self, File},
sync::Arc, io::Read,
};
use axum::{extract::Query, middleware::Next, response::Response, Extension};
use axum_macros::debug_handler;
use chrono::{Datelike, TimeZone, Utc};
use hyper::Request;
use ring::{rand::SystemRandom, signature::Ed25519KeyPair};
use tracing::debug;
use uuid::Uuid;
use std::io::Write;
use crate::{
errors::{NoneResult, ServiceError, StringResult, TokenResult},
State,
};
/**
* Takes in a request to create a new token with a secret key that gets printed
* to stdout and, if it matches, returns a valid PASETO token that can be used
* for future authentication
*/
#[debug_handler]
pub async fn begin(
Query(params): Query<HashMap<String, String>>,
Extension(state): Extension<Arc<State>>,
) -> TokenResult {
if let Some(k) = params.get("key") {
if k == &state.gen_key {
let dt = Utc::now();
let exp = Utc
.ymd(dt.year() + 1, dt.month(), dt.day())
.and_hms(0, 0, 0);
let kp = load_or_gen_keypair()?;
let token = match paseto::tokens::PasetoBuilder::new()
.set_ed25519_key(&kp)
.set_issued_at(Some(dt))
.set_expiration(&exp)
.set_issuer("solard")
.set_audience("solard")
.set_not_before(&Utc::now())
.build()
{
Ok(token) => token,
Err(_) => {
return Err(ServiceError::Generic(String::from(
"could not generate paseto key",
)));
}
};
return Ok(token.to_string());
} else {
return Err(ServiceError::NotAuthorized);
}
} else {
return Err(ServiceError::Generic("No key supplied".to_string()));
}
}
pub async fn requires_auth<B>(req: Request<B>, next: Next<B>) -> Result<Response, ServiceError> {
let auth_header = req
.headers()
.get(axum::http::header::AUTHORIZATION)
.and_then(|h| h.to_str().ok());
match auth_header {
Some(h) => {
debug!("Header: {}", h);
let kp = load_or_gen_keypair()?;
debug!("KP: {:?}", kp);
match paseto::tokens::validate_public_token(h, None, &paseto::tokens::PasetoPublicKey::ED25519KeyPair(&kp), &paseto::tokens::TimeBackend::Chrono) {
Ok(_) => Ok(next.run(req).await),
Err(_) => Err(ServiceError::NotAuthorized)
}
}
None => Err(ServiceError::NotAuthorized),
}
}
fn load_or_gen_keypair() -> Result<Ed25519KeyPair, ServiceError> {
let kp: Ed25519KeyPair;
let mut file = match File::open(".keypair") {
Ok(f) => f,
Err(_) => {
debug!("File does not exist, creating at .keypair");
File::create(".keypair").unwrap()
}
};
if let Ok(c) = fs::read(".keypair") {
if c.len() == 0 {
debug!("No keypair found. Generating...");
let srand = SystemRandom::new();
let pkcs8 = Ed25519KeyPair::generate_pkcs8(&srand)?;
fs::write(".keypair", pkcs8.as_ref()).unwrap();
debug!("Written keypair {:?} to .keypair", pkcs8.as_ref());
kp = Ed25519KeyPair::from_pkcs8(pkcs8.as_ref())?;
} else {
debug!("Found keypair file, contents: {:?}", c);
kp = Ed25519KeyPair::from_pkcs8(&c)?;
debug!("Loaded keypair from file");
}
} else {
debug!("Generating new keypair");
let srand = SystemRandom::new();
let pkcs8 = Ed25519KeyPair::generate_pkcs8(&srand)?;
fs::write(".keypair", pkcs8.as_ref()).unwrap();
debug!("Written keypair {:?} to .keypair", pkcs8.as_ref());
kp = Ed25519KeyPair::from_pkcs8(pkcs8.as_ref())?;
}
Ok(kp)
}