diff --git a/plume-cli/src/users.rs b/plume-cli/src/users.rs index 081b3b1f..18a7496c 100644 --- a/plume-cli/src/users.rs +++ b/plume-cli/src/users.rs @@ -109,16 +109,8 @@ fn new<'a>(args: &ArgMatches<'a>, conn: &Connection) { rpassword::read_password().expect("Couldn't read your password.") }); - NewUser::new_local( - conn, - username, - display_name, - admin, - &bio, - email, - User::hash_pass(&password).expect("Couldn't hash password"), - ) - .expect("Couldn't save new user"); + NewUser::new_local(conn, username, display_name, admin, &bio, email, &password) + .expect("Couldn't save new user"); } fn reset_password<'a>(args: &ArgMatches<'a>, conn: &Connection) { diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 2c07353d..fc6f18a0 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -333,17 +333,35 @@ impl User { }) } - pub fn hash_pass(pass: &str) -> Result { + fn hash_pass(pass: &str) -> Result { bcrypt::hash(pass, 10).map_err(Error::from) } - pub fn auth(&self, pass: &str) -> bool { + fn auth(&self, pass: &str) -> bool { self.hashed_password .clone() .map(|hashed| bcrypt::verify(pass, hashed.as_ref()).unwrap_or(false)) .unwrap_or(false) } + pub fn connect(rocket: &PlumeRocket, name: &str, password: &str) -> Result { + let user = User::find_by_email(&*rocket.conn, &name) + .or_else(|_| User::find_by_fqn(&rocket, &name)); + match user { + Ok(user) => { + if user.auth(password) { + Ok(user) + } else { + Err(Error::NotFound) + } + } + Err(_) => { + User::get(&rocket.conn, 1)?.auth(password); + Err(Error::NotFound) + } + } + } + pub fn reset_password(&self, conn: &Connection, pass: &str) -> Result<()> { diesel::update(self) .set(users::hashed_password.eq(User::hash_pass(pass)?)) @@ -923,7 +941,7 @@ impl NewUser { is_admin: bool, summary: &str, email: String, - password: String, + password: &str, ) -> Result { let (pub_key, priv_key) = gen_keypair(); User::insert( @@ -935,7 +953,7 @@ impl NewUser { summary: summary.to_owned(), summary_html: SafeString::new(&utils::md_to_html(&summary, None, false, None).0), email: Some(email), - hashed_password: Some(password), + hashed_password: Some(User::hash_pass(password)?), instance_id: Instance::get_local()?.id, ap_url: String::new(), public_key: String::from_utf8(pub_key).or(Err(Error::Signature))?, @@ -964,7 +982,7 @@ pub(crate) mod tests { true, "Hello there, I'm the admin", "admin@example.com".to_owned(), - "invalid_admin_password".to_owned(), + "invalid_admin_password", ) .unwrap(); let user = NewUser::new_local( @@ -974,7 +992,7 @@ pub(crate) mod tests { false, "Hello there, I'm no one", "user@example.com".to_owned(), - "invalid_user_password".to_owned(), + "invalid_user_password", ) .unwrap(); let other = NewUser::new_local( @@ -984,7 +1002,7 @@ pub(crate) mod tests { false, "Hello there, I'm someone else", "other@example.com".to_owned(), - "invalid_other_password".to_owned(), + "invalid_other_password", ) .unwrap(); vec![admin, user, other] @@ -1003,7 +1021,7 @@ pub(crate) mod tests { false, "Hello I'm a test", "test@example.com".to_owned(), - User::hash_pass("test_password").unwrap(), + "test_password", ) .unwrap(); @@ -1109,7 +1127,7 @@ pub(crate) mod tests { false, "Hello I'm a test", "test@example.com".to_owned(), - User::hash_pass("test_password").unwrap(), + "test_password", ) .unwrap(); diff --git a/src/api/mod.rs b/src/api/mod.rs index c320e081..7f34e6be 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -62,30 +62,20 @@ pub fn oauth( let conn = &*rockets.conn; let app = App::find_by_client_id(conn, &query.client_id)?; if app.client_secret == query.client_secret { - if let Ok(user) = User::find_by_fqn(&rockets, &query.username) { - if user.auth(&query.password) { - let token = ApiToken::insert( - conn, - NewApiToken { - app_id: app.id, - user_id: user.id, - value: random_hex(), - scopes: query.scopes.clone(), - }, - )?; - Ok(Json(json!({ - "token": token.value - }))) - } else { - Ok(Json(json!({ - "error": "Invalid credentials" - }))) - } + if let Ok(user) = User::connect(&rockets, &query.username, &query.password) { + let token = ApiToken::insert( + conn, + NewApiToken { + app_id: app.id, + user_id: user.id, + value: random_hex(), + scopes: query.scopes.clone(), + }, + )?; + Ok(Json(json!({ + "token": token.value + }))) } else { - // Making fake password verification to avoid different - // response times that would make it possible to know - // if a username is registered or not. - User::get(conn, 1)?.auth(&query.password); Ok(Json(json!({ "error": "Invalid credentials" }))) diff --git a/src/routes/session.rs b/src/routes/session.rs index 86a5b631..be7f3774 100644 --- a/src/routes/session.rs +++ b/src/routes/session.rs @@ -33,53 +33,29 @@ pub fn new(m: Option, rockets: PlumeRocket) -> Ructe { )) } -#[derive(Default, FromForm, Validate)] +#[derive(Default, FromForm)] pub struct LoginForm { - #[validate(length(min = "1", message = "We need an email, or a username to identify you"))] pub email_or_name: String, - #[validate(length(min = "1", message = "Your password can't be empty"))] pub password: String, } #[post("/login", data = "
")] pub fn create( form: LenientForm, - mut cookies: Cookies, rockets: PlumeRocket, + mut cookies: Cookies, ) -> RespondOrRedirect { let conn = &*rockets.conn; - let user = User::find_by_email(&*conn, &form.email_or_name) - .or_else(|_| User::find_by_fqn(&rockets, &form.email_or_name)); - let mut errors = match form.validate() { - Ok(_) => ValidationErrors::new(), - Err(e) => e, - }; - let user_id = if let Ok(user) = user { - if !user.auth(&form.password) { - let mut err = ValidationError::new("invalid_login"); - err.message = Some(Cow::from("Invalid username, or password")); - errors.add("email_or_name", err); - String::new() - } else { - user.id.to_string() - } + let user_id = if let Ok(user) = User::connect(&rockets, &form.email_or_name, &form.password) { + user.id.to_string() } else { - // Fake password verification, only to avoid different login times - // that could be used to see if an email adress is registered or not - User::get(&*conn, 1) - .map(|u| u.auth(&form.password)) - .expect("No user is registered"); - + let mut errors = ValidationErrors::new(); let mut err = ValidationError::new("invalid_login"); err.message = Some(Cow::from("Invalid username, or password")); errors.add("email_or_name", err); - String::new() - }; - - if !errors.is_empty() { return render!(session::login(&rockets.to_context(), None, &*form, errors)).into(); - } + }; cookies.add_private( Cookie::build(AUTH_COOKIE, user_id) @@ -111,7 +87,7 @@ pub fn create( &(conn, &rockets.intl.catalog, None, None), None, &*form, - errors + ValidationErrors::new() )) .into() } diff --git a/src/routes/user.rs b/src/routes/user.rs index b2316cb7..fc6c6922 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -520,7 +520,7 @@ pub fn create( false, "", form.email.to_string(), - User::hash_pass(&form.password).map_err(to_validation)?, + &form.password, ) .map_err(to_validation)?; Ok(Flash::success(