web-toolkit 0.1.1

A library providing some useful tools for backend web development.
Documentation
pub mod hasher {
    use std::io::{Error, ErrorKind};
    use argon2::{
        password_hash::{
            rand_core::OsRng,
            PasswordHash, PasswordHasher, PasswordVerifier, SaltString
        },
        Argon2
    };

    pub fn hash(passwd:&str) -> Result<String, Error> {
        let salt:SaltString = SaltString::generate(&mut OsRng);
        let argon:Argon2 = Argon2::default();
        match argon.hash_password(passwd.as_bytes(), &salt) {
            Ok(pass) => Ok(pass.to_string()),
            Err(e) => Err(Error::new(ErrorKind::Other, e.to_string()))
        }
    }

    pub fn verify(plain:&str, hashed: &str) -> Result<bool, std::io::Error> {
        let argon:Argon2 = Argon2::default();
        match PasswordHash::new(hashed) {
            Ok(ph) => {
                Ok(argon.verify_password(plain.as_bytes(), &ph).is_ok())
            },
            Err(e) => Err(Error::new(ErrorKind::Other, e.to_string()))
        }
    }
    
}

pub mod jwt {

    const PRIVATE:&str = "private_key.pem";
    const PUBLIC:&str = "public_key.pem";
    
    use std::{result::Result::Ok, str::FromStr, fmt::Display, collections::HashSet};
    use openssl::pkey::{PKey, Private};
    use std::{io::{Error, ErrorKind}, fs, path::Path};
    use jwt_simple::{prelude::{Ed25519KeyPair,Claims, VerificationOptions, Ed25519PublicKey, NoCustomClaims, EdDSAPublicKeyLike, Duration, EdDSAKeyPairLike, JWTClaims}};

    pub struct SignOptions<'a, T:ToString> {
        pub issuer: Option<&'a str>,
        pub expiry: Option<Duration>,
        pub audience: Option<T>
    }

    #[derive(Default)]
    pub struct VerifyOptions {
        pub valid_audiences: Option<HashSet<String>>,
        pub valid_issuers: Option<HashSet<String>>,
        pub valid_after_expiry: Option<Duration>
    }

    impl<'a, T:ToString+Copy> SignOptions<'a, T> {
        fn get_claim(&self) -> JWTClaims<NoCustomClaims> {
            let mut claim = Claims::create(match self.expiry {
                Some(exp) => exp,
                None => std::time::Duration::MAX.into()
            });

            claim = match self.audience {
                Some(audience) => claim.with_audience(audience),
                None => claim,
            };

            match self.issuer {
                Some(iss) => claim.with_issuer(iss),
                None => claim
            }
        }
    }

    pub fn generate_key_pair_if_absent() -> Result<(), Error> {

        let priv_key:PKey<Private> = match fs::read(PRIVATE) {
            Ok(key_in_bytes) => match PKey::private_key_from_pem(key_in_bytes.as_slice()){
                Ok(k) => k,
                Err(_) => match PKey::generate_ed25519() {
                    Ok(vv) => vv,
                    Err(e) => {
                        return Err(Error::new(ErrorKind::Other, e.to_string()));    
                    }
                },
            },
            Err(_) => match PKey::generate_ed25519() {
                Ok(val) => val,
                Err(e) => {
                    return Err(Error::new(ErrorKind::Other, e.to_string()));    
                }
            }
        };

        match priv_key.private_key_to_pem_pkcs8() {
            Ok(str) => {
                match fs::write(PRIVATE, str) {
                    Ok(_) => {},
                    Err(e) => {
                        return Err(Error::new(ErrorKind::Other, e.to_string()));    
                    }
                }
            },
            Err(e) => {
                return Err(Error::new(ErrorKind::Other, e.to_string()));    
            }
        };

        match priv_key.public_key_to_pem() {
            Ok(str) => {
                match fs::write(PUBLIC, str) {
                    Ok(_) => {},
                    Err(e) => {
                        return Err(Error::new(ErrorKind::Other, e.to_string()));    
                    }
                }
            },
            Err(e) => {
                return Err(Error::new(ErrorKind::Other, e.to_string()));    
            }
        };

        Ok(())

    }


    pub fn get_keys_as_str() -> Result<(String,String), Error> {
        let pub_bytes = match fs::read(Path::new("public_key.pem")) {
            Ok(bts) => bts,
            Err(e) => {return Err(Error::new(ErrorKind::Other, e.to_string()));}
        };

        let priv_bytes = match fs::read(Path::new("private_key.pem")) {
            Ok(bts) => bts,
            Err(e) => {return Err(Error::new(ErrorKind::Other, e.to_string()));}
        };

        Ok((String::from_utf8(priv_bytes).unwrap(),String::from_utf8(pub_bytes).unwrap()))
    }

    pub fn sign<T:ToString+Copy>(pem_str:&str, options:SignOptions<T>) -> Result<String, Error> {
        let key_pair = match Ed25519KeyPair::from_pem(pem_str) {
            Ok(pair) => pair,
            Err(e) => {
                return Err(Error::new(ErrorKind::Other, e.to_string()))
            }
        };

        let claim = options.get_claim();

        match key_pair.sign(claim) {
            Ok(token) => Ok(token),
            Err(e) => Err(Error::new(ErrorKind::Other, e.to_string()))
        }
    }

    pub fn verify<T>(token:&str, pub_pem_str:&str, options:VerifyOptions) -> Result<T, Error>
    where 
    <T as FromStr>::Err:Display,
    T:FromStr
    {
        let opts:VerificationOptions = VerificationOptions { accept_future:false, time_tolerance: options.valid_after_expiry, allowed_issuers: options.valid_issuers, allowed_audiences: options.valid_audiences, ..Default::default() };
        let pub_key = match Ed25519PublicKey::from_pem(pub_pem_str) {
            Ok(key) => key,
            Err(e) => {return Err(Error::new(ErrorKind::Other, e.to_string()))}
        };
        match pub_key.verify_token::<NoCustomClaims>(token, Some(opts)) {
            Ok(clm) => {
                let aud = match clm.audiences {
                    Some(adienc) => adienc,
                    None => {return Err(Error::new(ErrorKind::Other, "Empty audience"))}
                };
                match aud.into_string() {
                    Ok(au_clm) => match T::from_str(&au_clm) {
                        Ok(id) => Ok(id),
                        Err(e) => Err(Error::new(ErrorKind::Other, e.to_string()))
                    },
                    Err(e) => Err(Error::new(ErrorKind::Other, e.to_string()))
                }
            },
            Err(e) => Err(Error::new(ErrorKind::Other, e.to_string()))
        }
    }
}