@ -1,21 +1,35 @@
//! Authentication
//! Authentication
use super ::* ;
use super ::* ;
use tokio ::time ;
use std ::{ error , fmt } ;
#[ derive(Debug, Clone, PartialEq, Eq, Hash) ]
pub struct Sha256Hash ( pub sha256 ::Sha256Hash ) ;
pub struct Sha256Hash ( pub sha256 ::Sha256Hash ) ;
type RsaSignature = rsa ::Signature ;
type RsaSignature = rsa ::Signature ;
#[ derive(Debug) ]
pub struct DecodeTokenError ;
impl str ::FromStr for Sha256Hash
impl str ::FromStr for Sha256Hash
{
{
type Err = ( ) ;
type Err = DecodeTokenError ;
fn from_str ( s : & str ) -> Result < Self , Self ::Err > {
fn from_str ( s : & str ) -> Result < Self , Self ::Err > {
todo! ( ) //read encoded base64(?)/hex into `Signature`
conv ::ModifiedBase64String ::try_from_base64 ( s ) . map_err ( | _ | DecodeTokenError ) . and_then ( | md | {
let mut output = sha256 ::Sha256Hash ::default ( ) ;
if md . decode ( output . as_mut ( ) ) = = sha256 ::SIZE {
Ok ( Self ( output ) )
} else {
Err ( DecodeTokenError )
}
} )
}
}
}
}
#[ derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize) ]
#[ derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize) ]
pub struct AuthRequest
pub struct AuthRequest
{
{
id : Uuid ,
pub id : Uuid ,
sign_this : [ u8 ; 32 ] ,
sign_this : [ u8 ; 32 ] ,
salt : [ u8 ; 16 ] ,
salt : [ u8 ; 16 ] ,
@ -25,6 +39,21 @@ pub struct AuthRequest
impl AuthRequest
impl AuthRequest
{
{
pub fn hash_password ( & self , _state : & State , passwd : & str ) -> sha256 ::Sha256Hash
{
// NOTE: _state will be used when we have a 2nd global salt as well, for now, ignore it.
sha256 ::compute_slices ( [ passwd . as_bytes ( ) , & self . salt [ .. ] ] . iter ( ) )
}
}
impl AuthRequest
{
/// The TTL for this auth request
pub fn ttl ( & self ) -> time ::Duration
{
time ::Duration ::from_millis ( self . ttl_ms )
}
/// Create a new auth request
/// Create a new auth request
pub fn new ( cfg : & settings ::Settings ) -> Self
pub fn new ( cfg : & settings ::Settings ) -> Self
{
{
@ -45,22 +74,107 @@ pub async fn auth_req(who: source::IpAddr, state: Arc<State>) -> Result<AuthRequ
{
{
let req = AuthRequest ::new ( state . cfg ( ) ) ;
let req = AuthRequest ::new ( state . cfg ( ) ) ;
trace ! ( "{:?} auth req" , who ) ;
trace ! ( "{:?} auth req" , who ) ;
// Add `req` into `state` auth hashmap (`req.id` is key) for verification.
// Use `DelayQueue` to remove `req.id` from the hashmap after `ttl` expires.
{
let mut auth = state . auth_tokens ( ) . await ;
auth . insert_req ( req . clone ( ) ) ;
}
// TODO: Add `req` into `state` auth hashmap (`req.id` is key) for verification.
// TODO: Use `DelayQueue` to remove `req.id` from the hashmap after `ttl` expires.
Ok ( req )
Ok ( req )
}
}
pub async fn auth_key( who: source ::IpAddr , state: Arc < State > , req_id : Uuid , num: usize , body : Bytes ) -> Result < ( ) , Infallible >
async fn real_ auth_key( state: Arc < State > , req_id : Uuid , sigs: impl IntoIterator < Item = RsaSignature > ) -> Result < ( ) , AuthError >
{
{
trace ! ( "{:?} auth resp key <{}>:{}" , who , req_id , num ) ;
Ok ( ( ) )
Ok ( ( ) )
}
}
pub async fn auth_key ( who : source ::IpAddr , state : Arc < State > , req_id : Uuid , num : usize , body : Bytes ) -> Result < ( ) , warp ::Rejection >
{
trace ! ( "{:?} auth resp key <{}>:{}" , who , req_id , num ) ;
//TODO: Read keys from body, pass to `real_auth_key`.
todo! ( )
}
pub async fn auth_pass ( who : source ::IpAddr , state : Arc < State > , req_id : Uuid , passhash : sha256 ::Sha256Hash ) -> Result < ( ) , Infallible >
async fn real_ auth_pass( state : Arc < State > , req_id : Uuid , passhash : sha256 ::Sha256Hash ) -> Result < ( ) , AuthError >
{
{
trace ! ( "{:?} auth resp pass <{}>: \"{}\"" , who , req_id , passhash ) ;
let req = {
state . auth_tokens ( ) . await . handle_req ( req_id ) ?
} ;
if ! req . passwd_is_allowed {
return Err ( AuthError ::Method ) ;
}
//TODO: Grab valid password hash from `State` and compare
//TODO: Generate real authoriseation token that maps to whichever user was authorised, insert into state with a TTL that gets refreshed when the token is used.
Ok ( ( ) )
Ok ( ( ) )
}
}
pub async fn auth_pass ( who : source ::IpAddr , state : Arc < State > , req_id : Uuid , passhash : sha256 ::Sha256Hash ) -> Result < ( ) , warp ::Rejection >
{
trace ! ( "{:?} auth resp pass <{}>: \"{}\"" , who , req_id , passhash ) ;
real_auth_pass ( state , req_id , passhash ) . await . map_err ( warp ::reject ::custom )
}
#[ derive(Debug) ]
pub enum AuthError
{
Id ,
Hash ,
Sig ,
Method ,
Internal ,
}
impl AuthError
{
/// A warp recovery filter for auth errors
pub async fn recover ( err : warp ::Rejection ) -> Result < impl warp ::Reply , warp ::Rejection >
{
use warp ::http ::StatusCode ;
if let Some ( this ) = err . find ::< Self > ( ) {
let code = match this {
Self ::Id
= > return Err ( warp ::reject ::not_found ( ) ) ,
Self ::Hash
| Self ::Sig
= > StatusCode ::FORBIDDEN ,
Self ::Method
= > StatusCode ::METHOD_NOT_ALLOWED ,
_ = > StatusCode ::INTERNAL_SERVER_ERROR ,
} ;
Ok ( warp ::reply ::with_status ( format! ( "auth failed: {}" , this ) , code ) )
} else {
Err ( err )
}
}
}
impl error ::Error for AuthError { }
impl warp ::reject ::Reject for AuthError { }
impl fmt ::Display for AuthError
{
fn fmt ( & self , f : & mut fmt ::Formatter < ' _ > ) -> fmt ::Result
{
match self
{
Self ::Id = > write! ( f , "invalid response id" ) ,
Self ::Hash = > write! ( f , "no matching hash" ) ,
Self ::Sig = > write! ( f , "no matching signature" ) ,
Self ::Method = > write! ( f , "auth method not allowed" ) ,
_ = > write! ( f , "internal error" ) ,
}
}
}
impl From < state ::AuthCacheError > for AuthError
{
fn from ( _ : state ::AuthCacheError ) -> Self
{
Self ::Id
}
}