|
@ -4,7 +4,7 @@ use rocket_contrib::json::Json; |
|
|
|
|
|
|
|
|
use crate::api::core::two_factor::_generate_recover_code; |
|
|
use crate::api::core::two_factor::_generate_recover_code; |
|
|
use crate::api::{EmptyResult, JsonResult, JsonUpcase, NumberOrString, PasswordData}; |
|
|
use crate::api::{EmptyResult, JsonResult, JsonUpcase, NumberOrString, PasswordData}; |
|
|
use crate::auth::Headers; |
|
|
use crate::auth::{ClientIp, Headers}; |
|
|
use crate::crypto; |
|
|
use crate::crypto; |
|
|
use crate::db::{ |
|
|
use crate::db::{ |
|
|
models::{TwoFactor, TwoFactorType}, |
|
|
models::{TwoFactor, TwoFactorType}, |
|
@ -54,7 +54,12 @@ struct EnableAuthenticatorData { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
#[post("/two-factor/authenticator", data = "<data>")] |
|
|
#[post("/two-factor/authenticator", data = "<data>")] |
|
|
fn activate_authenticator(data: JsonUpcase<EnableAuthenticatorData>, headers: Headers, conn: DbConn) -> JsonResult { |
|
|
fn activate_authenticator( |
|
|
|
|
|
data: JsonUpcase<EnableAuthenticatorData>, |
|
|
|
|
|
headers: Headers, |
|
|
|
|
|
ip: ClientIp, |
|
|
|
|
|
conn: DbConn, |
|
|
|
|
|
) -> JsonResult { |
|
|
let data: EnableAuthenticatorData = data.into_inner().data; |
|
|
let data: EnableAuthenticatorData = data.into_inner().data; |
|
|
let password_hash = data.MasterPasswordHash; |
|
|
let password_hash = data.MasterPasswordHash; |
|
|
let key = data.Key; |
|
|
let key = data.Key; |
|
@ -77,7 +82,7 @@ fn activate_authenticator(data: JsonUpcase<EnableAuthenticatorData>, headers: He |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Validate the token provided with the key, and save new twofactor
|
|
|
// Validate the token provided with the key, and save new twofactor
|
|
|
validate_totp_code(&user.uuid, token, &key.to_uppercase(), &conn)?; |
|
|
validate_totp_code(&user.uuid, token, &key.to_uppercase(), &ip, &conn)?; |
|
|
|
|
|
|
|
|
_generate_recover_code(&mut user, &conn); |
|
|
_generate_recover_code(&mut user, &conn); |
|
|
|
|
|
|
|
@ -89,20 +94,31 @@ fn activate_authenticator(data: JsonUpcase<EnableAuthenticatorData>, headers: He |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
#[put("/two-factor/authenticator", data = "<data>")] |
|
|
#[put("/two-factor/authenticator", data = "<data>")] |
|
|
fn activate_authenticator_put(data: JsonUpcase<EnableAuthenticatorData>, headers: Headers, conn: DbConn) -> JsonResult { |
|
|
fn activate_authenticator_put( |
|
|
activate_authenticator(data, headers, conn) |
|
|
data: JsonUpcase<EnableAuthenticatorData>, |
|
|
|
|
|
headers: Headers, |
|
|
|
|
|
ip: ClientIp, |
|
|
|
|
|
conn: DbConn, |
|
|
|
|
|
) -> JsonResult { |
|
|
|
|
|
activate_authenticator(data, headers, ip, conn) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
pub fn validate_totp_code_str(user_uuid: &str, totp_code: &str, secret: &str, conn: &DbConn) -> EmptyResult { |
|
|
pub fn validate_totp_code_str( |
|
|
|
|
|
user_uuid: &str, |
|
|
|
|
|
totp_code: &str, |
|
|
|
|
|
secret: &str, |
|
|
|
|
|
ip: &ClientIp, |
|
|
|
|
|
conn: &DbConn, |
|
|
|
|
|
) -> EmptyResult { |
|
|
let totp_code: u64 = match totp_code.parse() { |
|
|
let totp_code: u64 = match totp_code.parse() { |
|
|
Ok(code) => code, |
|
|
Ok(code) => code, |
|
|
_ => err!("TOTP code is not a number"), |
|
|
_ => err!("TOTP code is not a number"), |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
validate_totp_code(user_uuid, totp_code, secret, &conn) |
|
|
validate_totp_code(user_uuid, totp_code, secret, ip, &conn) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
pub fn validate_totp_code(user_uuid: &str, totp_code: u64, secret: &str, conn: &DbConn) -> EmptyResult { |
|
|
pub fn validate_totp_code(user_uuid: &str, totp_code: u64, secret: &str, ip: &ClientIp, conn: &DbConn) -> EmptyResult { |
|
|
use oath::{totp_raw_custom_time, HashType}; |
|
|
use oath::{totp_raw_custom_time, HashType}; |
|
|
|
|
|
|
|
|
let decoded_secret = match BASE32.decode(secret.as_bytes()) { |
|
|
let decoded_secret = match BASE32.decode(secret.as_bytes()) { |
|
@ -144,11 +160,22 @@ pub fn validate_totp_code(user_uuid: &str, totp_code: u64, secret: &str, conn: & |
|
|
twofactor.save(&conn)?; |
|
|
twofactor.save(&conn)?; |
|
|
return Ok(()); |
|
|
return Ok(()); |
|
|
} else if generated == totp_code && time_step <= twofactor.last_used as i64 { |
|
|
} else if generated == totp_code && time_step <= twofactor.last_used as i64 { |
|
|
warn!("This or a TOTP code within {} steps back and forward has already been used!", steps); |
|
|
warn!( |
|
|
err!(format!("Invalid TOTP code! Server time: {}", current_time.format("%F %T UTC"))); |
|
|
"This or a TOTP code within {} steps back and forward has already been used!", |
|
|
|
|
|
steps |
|
|
|
|
|
); |
|
|
|
|
|
err!(format!( |
|
|
|
|
|
"Invalid TOTP code! Server time: {} IP: {}", |
|
|
|
|
|
current_time.format("%F %T UTC"), |
|
|
|
|
|
ip.ip |
|
|
|
|
|
)); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Else no valide code received, deny access
|
|
|
// Else no valide code received, deny access
|
|
|
err!(format!("Invalid TOTP code! Server time: {}", current_time.format("%F %T UTC"))); |
|
|
err!(format!( |
|
|
|
|
|
"Invalid TOTP code! Server time: {} IP: {}", |
|
|
|
|
|
current_time.format("%F %T UTC"), |
|
|
|
|
|
ip.ip |
|
|
|
|
|
)); |
|
|
} |
|
|
} |
|
|