medal/
helpers.rs

1/*  medal                                                                                                            *\
2 *  Copyright (C) 2022  Bundesweite Informatikwettbewerbe, Robert Czechowski                                                            *
3 *                                                                                                                   *
4 *  This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero        *
5 *  General Public License as published  by the Free Software Foundation, either version 3 of the License, or (at    *
6 *  your option) any later version.                                                                                  *
7 *                                                                                                                   *
8 *  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the       *
9 *  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public      *
10 *  License for more details.                                                                                        *
11 *                                                                                                                   *
12 *  You should have received a copy of the GNU Affero General Public License along with this program.  If not, see   *
13\*  <http://www.gnu.org/licenses/>.                                                                                  */
14
15extern crate bcrypt;
16
17use rand::{
18    distributions::{Alphanumeric, Distribution},
19    thread_rng, Rng,
20};
21
22struct LowercaseAlphanumeric;
23impl Distribution<char> for LowercaseAlphanumeric {
24    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> char {
25        const RANGE: u32 = 26 + 10;
26        const GEN_ASCII_LOWERCASE_STR_CHARSET: &[u8] = b"abcdefghijklmnopqrstuvwxyz0123456789";
27
28        let var: u32 = rng.gen_range(0, RANGE);
29        GEN_ASCII_LOWERCASE_STR_CHARSET[var as usize] as char
30    }
31}
32
33use core::MedalError;
34use db_objects::SessionUser;
35
36pub fn make_ambiguous_code(len: usize) -> String { thread_rng().sample_iter(&Alphanumeric).take(len).collect() }
37
38pub fn make_unambiguous_lowercase_code(len: usize) -> String {
39    thread_rng().sample_iter(&LowercaseAlphanumeric)
40                .filter(|x| {
41                    let x = *x;
42                    !(x == 'l' || x == '1' || x == 'o' || x == '0')
43                })
44                .take(len)
45                .collect()
46}
47
48pub fn make_unambiguous_lowercase_code_prefix(len: usize, prefix: &str) -> String {
49    let mut code = prefix.to_owned();
50    code.push_str(&make_unambiguous_lowercase_code(len));
51    code
52}
53
54pub fn make_session_token() -> String { make_ambiguous_code(10) }
55
56pub fn make_csrf_token() -> String { make_ambiguous_code(10) }
57
58pub fn make_salt() -> String { make_ambiguous_code(10) }
59
60pub fn make_filename_secret() -> String { make_ambiguous_code(10) }
61
62pub fn make_groupcode() -> String { make_unambiguous_lowercase_code_prefix(7, "g") } // 1 week @ 10/s, about 5700 groups
63
64pub fn make_logincode() -> String { make_unambiguous_lowercase_code_prefix(9, "u") } // 1 y @ 10/s, about 110000 users
65
66pub fn make_admincode() -> String { make_unambiguous_lowercase_code_prefix(10, "a") } // 3.6 My @ 10/s, 1 admin
67
68pub fn hash_password(password: &str, salt: &str) -> Result<String, MedalError> {
69    let password_and_salt = [password, salt].concat();
70    match bcrypt::hash(password_and_salt, 5) {
71        Ok(result) => Ok(result),
72        Err(_) => Err(MedalError::PasswordHashingError),
73    }
74}
75
76pub fn verify_password(password: &str, salt: &str, password_hash: &str) -> bool {
77    let password_and_salt = [password, salt].concat();
78    bcrypt::verify(password_and_salt, password_hash).unwrap_or_default()
79}
80
81pub trait SetPassword {
82    fn set_password(&mut self, password: &str) -> Option<()>;
83}
84
85impl SetPassword for SessionUser {
86    fn set_password(&mut self, password: &str) -> Option<()> {
87        let salt: String = thread_rng().sample_iter(&Alphanumeric).take(10).collect();
88        let hash = hash_password(password, &salt).ok()?;
89
90        self.password = Some(hash);
91        self.salt = Some(salt);
92        Some(())
93    }
94}