139 lines
3.9 KiB
Rust
Raw Normal View History

2022-10-10 23:07:40 +02:00
use lettre::Transport;
use once_cell::sync::Lazy;
2022-10-10 23:07:40 +02:00
use ring::rand::SecureRandom;
use rustracing_jaeger::Span;
use tiny_http::{Request, ResponseBox};
use crate::{
config::CONFIG,
db,
db::{DBConnection, TfaTypes},
dto,
metrics,
routes::{filters::UserInfo, get_reply, AppError}
};
2022-10-10 23:07:40 +02:00
fn build_mail_sender() -> lettre::SmtpTransport {
lettre::SmtpTransport::builder_dangerous(CONFIG.smtp_server.clone())
.port(CONFIG.smtp_port)
.tls(lettre::transport::smtp::client::Tls::Required(
lettre::transport::smtp::client::TlsParameters::new(CONFIG.smtp_server.clone()).unwrap()
))
.credentials(lettre::transport::smtp::authentication::Credentials::new(
CONFIG.smtp_user.clone(),
CONFIG.smtp_password.clone()
))
2022-10-10 23:07:40 +02:00
.build()
}
2022-10-14 03:15:57 +02:00
pub static MAIL_SENDER: Lazy<lettre::SmtpTransport> = Lazy::new(build_mail_sender);
2022-10-10 23:07:40 +02:00
fn get_totp(user: &db::User) -> totp_rs::TOTP {
2022-10-10 23:07:40 +02:00
totp_rs::TOTP::from_rfc6238(
totp_rs::Rfc6238::new(
6,
user.tfa_secret.clone().unwrap(),
Some("MFileserver".to_owned()),
user.name.clone()
)
.unwrap()
)
.unwrap()
2022-10-10 23:07:40 +02:00
}
pub fn verify2fa(user: &db::User, code: String) -> bool {
let allowed_skew = if user.tfa_type == TfaTypes::Totp {
0
} else {
10
};
2022-10-10 23:07:40 +02:00
let totp = get_totp(user);
let time = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs();
let base_step = time / totp.step - allowed_skew;
for i in 0..allowed_skew + 1 {
let step = (base_step + i) * totp.step;
if totp.generate(step).eq(&code) {
return true;
}
}
false
}
pub fn send_2fa_mail(span: &Span, user: &db::User) {
let _span = metrics::span("send_2fa_mail", span);
2022-10-10 23:07:40 +02:00
let totp = get_totp(user);
let code = totp.generate_current().unwrap();
let mail = lettre::Message::builder()
.from("fileserver@mattv.de".parse().unwrap())
.to(user.name.parse().unwrap())
.subject("MFileserver - Email 2fa code")
.body(format!(
"Your code is: {}\r\nIt is valid for 5 minutes",
code
))
2022-10-10 23:07:40 +02:00
.unwrap();
MAIL_SENDER.send(&mail).expect("Failed to send mail");
}
pub fn tfa_setup(
span: &Span,
_: &mut Request,
db: &mut DBConnection,
mut info: UserInfo,
data: dto::requests::TfaSetup
) -> Result<ResponseBox, AppError> {
2022-10-10 23:07:40 +02:00
let mut secret: [u8; 32] = [0; 32];
super::SEC_RANDOM.fill(&mut secret).expect("Failed to generate secret");
let secret = Vec::from(secret);
info.0.tfa_secret = Some(secret);
db.save_user(span, &info.0);
2022-10-10 23:07:40 +02:00
if data.mail {
send_2fa_mail(span, &info.0);
get_reply(&dto::responses::Success { statusCode: 200 })
2022-10-10 23:07:40 +02:00
} else {
let totp = get_totp(&info.0);
get_reply(&dto::responses::TfaSetup {
statusCode: 200,
secret: totp.get_secret_base32(),
qrCode: "data:image/png;base64,".to_owned() + &totp.get_qr().expect("Failed to generate qr code")
})
}
}
pub fn tfa_complete(
span: &Span,
_: &mut Request,
db: &mut DBConnection,
mut info: UserInfo,
data: dto::requests::TfaComplete
) -> Result<ResponseBox, AppError> {
info.0.tfa_type = if data.mail {
TfaTypes::Email
} else {
TfaTypes::Totp
};
2022-10-10 23:07:40 +02:00
if verify2fa(&info.0, data.code) {
db.save_user(span, &info.0);
db.delete_all_tokens(span, info.0.id);
get_reply(&dto::responses::Success { statusCode: 200 })
2022-10-10 23:07:40 +02:00
} else {
AppError::BadRequest("Incorrect 2fa code").err()
}
}
pub fn tfa_disable(
span: &Span,
_: &mut Request,
db: &mut DBConnection,
mut info: UserInfo
) -> Result<ResponseBox, AppError> {
2022-10-10 23:07:40 +02:00
info.0.tfa_secret = None;
info.0.tfa_type = TfaTypes::None;
db.save_user(span, &info.0);
db.delete_all_tokens(span, info.0.id);
get_reply(&dto::responses::Success { statusCode: 200 })
2022-10-10 23:07:40 +02:00
}