1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
use ::log::*;
use bcrypt::verify;
use std::{thread, time};
use std::collections::HashMap;
use crate::db;
use crate::utils;
use crate::messages::Msg;
#[derive(Debug)]
pub struct Login {
email: String,
password: String,
status: String,
}
impl Login {
pub fn authenticate(email: &str, password: &str) -> Result<Msg, Msg> {
debug!("Inside login::authenticate({:?}, {:?})", email, password);
let login = Login {
email: email.to_string(),
password: password.to_string(),
status: String::new()
};
login.process()
}
pub fn email(&self) -> String {
self.email.clone()
}
pub fn password(&self) -> String {
self.password.clone()
}
fn process(&self) -> Result<Msg, Msg> {
debug!("Inside login.process()...");
let user = db::get_user_for(&self.email, &Msg::Active.description()).unwrap_or(db::User::default());
debug!("User received: {:?}", &user);
if user.email().is_empty() {
debug!("User is empty...returning InvalidCredentials");
return Err(Msg::InvalidCredentials);
}
debug!("User is: {:?}", &user);
debug!("Verify between self password: {:?} and user's {:?}", &self.password(), &user.password());
if verify(&self.password(), &user.password()).unwrap() {
debug!("Login Success");
Ok(Msg::LoginSucceeded)
} else {
error!("Invalid password");
Err(Msg::InvalidCredentials)
}
}
pub fn forgot_password(email: &str) -> Result<Msg, Msg> {
debug!("forgot_password({:?})............................<<", email);
if db::email_record_count(email) < 1 {
return Err(Msg::EmailNotExist);
}
let mut user = db::get_user_for_email(email)?;
let result = Msg::key_for(&user.status());
debug!("Msg key: {:?}", &result);
match result {
Msg::Cancelled => Err(Msg::Cancelled),
Msg::Dormant => Err(Msg::Dormant),
Msg::ConfirmationPending => Err(Msg::ConfirmationPending),
_ => {
user.set_status(&Msg::ForgotPasswordPending.description());
let token = utils::generate_token();
let row_count = user.update_with_token(&token);
debug!("{:?} row(s) updated in user with token for forgot password", &row_count);
Self::send_forgot_password_email(&user.email(), &token);
if row_count == 1 {
Ok(Msg::ForgotPasswordProcessed)
} else {
Err(Msg::ForgotPasswordFailed)
}
}
}
}
pub fn forgot_password_expired(email: &str) -> Result<Msg, Msg> {
let mut user = db::get_user_for(email, &Msg::ForgotPasswordPending.description()).unwrap_or(db::User::default());
if user.email().is_empty() { return Err(Msg::ForgotPasswordFailed) }
user.set_status(&Msg::Active.description());
let row_count = user.clear_token();
if row_count == 1 {
Ok(Msg::ForgotPasswordTokenExpired)
} else {
Err(Msg::ForgotPasswordFailed)
}
}
fn send_forgot_password_email(email_to: &str, token: &str) {
let email_enabled: bool = super::app_config("email_enabled") == "true".to_string();
if !email_enabled { return (); }
let email_from = super::app_config("email_from");
let message_body = format!("Forgot Password Token: {}.\n\nPlease copy this token into Forgot Password Confirmation window to complete reset process", &token);
let subject = "Login-app: Forgot Password regarding...";
let email = utils::Email::new(&email_from, &email_to, &subject, &message_body);
email.send();
}
pub fn reset_password(map: &HashMap<String, String>, email: String) -> Result<Msg, Vec<Msg>> {
let new_pwd = map.get("password").unwrap();
let repeat_pwd = map.get("repeat-password").unwrap();
if new_pwd != repeat_pwd {
return Err(vec![Msg::PasswordsDoNotMatch])
}
let messages = utils::Validator::validate_password(new_pwd.to_string());
if !messages.is_empty() { return Err(messages); }
let mut user = db::get_user_for(&email, &Msg::ForgotPasswordPending.description()).unwrap_or(db::User::default());
if user.email().is_empty() { return Err( vec![Msg::ResetPasswordFailed] ) }
user.set_status(&Msg::Active.description());
let hashed_pwd = utils::hash(&new_pwd);
user.set_password(&hashed_pwd);
let count = user.update_for_password();
debug!("{:?} row(s) affected for user.update_for_password", &count);
if count != 1 { return Err( vec![Msg::ResetPasswordFailed] ) };
Ok(Msg::ResetPasswordProcessed)
}
pub fn wait_to_expire_forgot_password_token(delay: u64, email: String ) {
debug!("wait_to_expire_forgot_password_token({:?}, {:?}).......................<<", &delay, &email);
thread::spawn(move || {
let duration = time::Duration::from_millis(delay+3000);
debug!("Time before sleep: {:?}", time::Instant::now());
thread::sleep(duration);
debug!("Time after sleep: {:?}", time::Instant::now());
let result = Self::forgot_password_expired(&email);
match result {
Ok(msg) => debug!("Successfully expired forgot password token: {:?}", msg),
Err(msg) => error!("Failed to expire forgot password token: {:?}", msg),
}
});
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::registration::Registration;
use crate::messages::Msg;
#[test]
fn utest_email_not_exist() {
let login = Login {
email: "invalid email $ unit - test".to_string(),
password: "".to_string(),
status: "".to_string(),
};
let result = login.process().unwrap_or_else(|error| error);
assert_eq!(result, Msg::InvalidCredentials);
}
#[test]
fn utest_invalid_credentials() {
let email = "invalid_credentials@unit.tst";
Registration::new(&email, "Pass123~", "Pass123~").unwrap_or_else(|_| Msg::NewRegistrationFailed);
let login = Login {
email: email.to_string(),
password: "".to_string(),
status: "".to_string(),
};
let result = login.process().unwrap_or_else(|error| error);
assert_eq!(result, Msg::InvalidCredentials);
Registration::cancel(&email).unwrap_or_else(|error| error);
}
}