Can register Authenticator app
This commit is contained in:
src
templates/settings
@ -1,14 +1,19 @@
|
||||
use std::io::ErrorKind;
|
||||
|
||||
use base32::Alphabet;
|
||||
use rand::Rng;
|
||||
use totp_rfc6238::{HashAlgorithm, TotpGenerator};
|
||||
|
||||
use crate::data::app_config::AppConfig;
|
||||
use crate::data::user::User;
|
||||
use crate::utils::err::Res;
|
||||
use crate::utils::time::time;
|
||||
|
||||
const BASE32_ALPHABET: Alphabet = Alphabet::RFC4648 { padding: true };
|
||||
const NUM_DIGITS: i32 = 6;
|
||||
const PERIOD: i32 = 30;
|
||||
const NUM_DIGITS: usize = 6;
|
||||
const PERIOD: u64 = 30;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize, Debug)]
|
||||
pub struct TotpKey {
|
||||
encoded: String,
|
||||
}
|
||||
@ -17,11 +22,16 @@ impl TotpKey {
|
||||
/// Generate a new TOTP key
|
||||
pub fn new_random() -> Self {
|
||||
let random_bytes = rand::thread_rng().gen::<[u8; 10]>();
|
||||
TotpKey {
|
||||
Self {
|
||||
encoded: base32::encode(BASE32_ALPHABET, &random_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a key from an encoded secret
|
||||
pub fn from_encoded_secret(s: &str) -> Self {
|
||||
Self { encoded: s.to_string() }
|
||||
}
|
||||
|
||||
/// Get QrCode URL for user
|
||||
///
|
||||
/// Based on https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
||||
@ -50,4 +60,38 @@ impl TotpKey {
|
||||
pub fn get_secret(&self) -> String {
|
||||
self.encoded.to_string()
|
||||
}
|
||||
|
||||
/// Get current code
|
||||
pub fn current_code(&self) -> Res<String> {
|
||||
self.get_code_at(|| time())
|
||||
}
|
||||
|
||||
/// Get previous code
|
||||
pub fn previous_code(&self) -> Res<String> {
|
||||
self.get_code_at(|| time() - PERIOD)
|
||||
}
|
||||
|
||||
/// Get the code at a specific time
|
||||
fn get_code_at<F: Fn() -> u64>(&self, get_time: F) -> Res<String> {
|
||||
let gen = TotpGenerator::new()
|
||||
.set_digit(NUM_DIGITS).unwrap()
|
||||
.set_step(PERIOD).unwrap()
|
||||
.set_hash_algorithm(HashAlgorithm::SHA1)
|
||||
.build();
|
||||
|
||||
let key = match base32::decode(BASE32_ALPHABET, &self.encoded) {
|
||||
None => {
|
||||
return Err(Box::new(
|
||||
std::io::Error::new(ErrorKind::Other, "Failed to decode base32 secret!")));
|
||||
}
|
||||
Some(k) => k,
|
||||
};
|
||||
|
||||
Ok(gen.get_code_with(&key, get_time))
|
||||
}
|
||||
|
||||
/// Check a code's validity
|
||||
pub fn check_code(&self, code: &str) -> Res<bool> {
|
||||
Ok(self.current_code()?.eq(code) || self.previous_code()?.eq(code))
|
||||
}
|
||||
}
|
@ -1,9 +1,15 @@
|
||||
use crate::data::client::ClientID;
|
||||
use crate::data::entity_manager::EntityManager;
|
||||
use crate::data::totp_key::TotpKey;
|
||||
use crate::utils::err::Res;
|
||||
|
||||
pub type UserID = String;
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum SecondFactor {
|
||||
TOTP(TotpKey)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct User {
|
||||
pub uid: UserID,
|
||||
@ -16,6 +22,9 @@ pub struct User {
|
||||
pub enabled: bool,
|
||||
pub admin: bool,
|
||||
|
||||
/// 2FA
|
||||
pub second_factors: Option<Vec<SecondFactor>>,
|
||||
|
||||
/// None = all services
|
||||
/// Some([]) = no service
|
||||
pub authorized_clients: Option<Vec<ClientID>>,
|
||||
@ -36,6 +45,14 @@ impl User {
|
||||
pub fn verify_password<P: AsRef<[u8]>>(&self, pass: P) -> bool {
|
||||
verify_password(pass, &self.password)
|
||||
}
|
||||
|
||||
pub fn add_factor(&mut self, factor: SecondFactor) {
|
||||
if self.second_factors.is_none() {
|
||||
self.second_factors = Some(vec![]);
|
||||
}
|
||||
|
||||
self.second_factors.as_mut().unwrap().push(factor);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for User {
|
||||
@ -58,6 +75,7 @@ impl Default for User {
|
||||
need_reset_password: false,
|
||||
enabled: true,
|
||||
admin: false,
|
||||
second_factors: Some(vec![]),
|
||||
authorized_clients: Some(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user