aboutsummaryrefslogblamecommitdiff
path: root/src/handlers/auth.rs
blob: cafaeb890e36b8a752b588bbbebd4b0368b1bbe6 (plain) (tree)
1
2
3
4
5
6
7
8
9
10




                         
 
                                                                            
                               

                                      
                                                          

               

                   



                                                                  






                                                                               



                                                  

                                        












                                                                  








                                                                  
 
                                         





                                                                         

 










                                                                                                 








                                                                  



                                                        



                                                         
 
          
 
use std::{
    collections::HashMap,
    fs::{self, File},
    sync::Arc,
};

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 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) => Ok(next.run(req).await),
        None => Err(ServiceError::NotAuthorized),
    }
}

fn load_or_gen_keypair() -> Result<Ed25519KeyPair, ServiceError> {
    let kp: Ed25519KeyPair;
    if let Ok(c) = fs::read_to_string(".keypair") {
        kp = Ed25519KeyPair::from_pkcs8(&hex::decode(c)?)?;
    } else {
        let srand = SystemRandom::new();
        let pkcs8 = Ed25519KeyPair::generate_pkcs8(&srand)?;

        let mut file = match File::open(".keypair") {
            Ok(f) => f,
            Err(_) => File::create(".keypair").unwrap(),
        };
        file.write(pkcs8.as_ref());

        kp = Ed25519KeyPair::from_pkcs8(pkcs8.as_ref())?;
    }

    Ok(kp)
}