From 864febeefc5213928bf88f89a714a006326e5b41 Mon Sep 17 00:00:00 2001 From: Owen Jacobson Date: Fri, 11 Oct 2024 21:16:11 -0400 Subject: Stop creating accounts during login. --- src/app.rs | 2 +- src/login/routes/test/login.rs | 32 ++++++++++-------------------- src/token/app.rs | 44 ++++++++++++------------------------------ src/token/repo/auth.rs | 10 ++-------- 4 files changed, 25 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/app.rs b/src/app.rs index cf49070..69831ee 100644 --- a/src/app.rs +++ b/src/app.rs @@ -58,6 +58,6 @@ impl App { } pub const fn tokens(&self) -> Tokens { - Tokens::new(&self.db, &self.events, &self.token_events) + Tokens::new(&self.db, &self.token_events) } } diff --git a/src/login/routes/test/login.rs b/src/login/routes/test/login.rs index 6a3b79c..68c92de 100644 --- a/src/login/routes/test/login.rs +++ b/src/login/routes/test/login.rs @@ -6,16 +6,16 @@ use axum::{ use crate::{login::routes, test::fixtures, token::app}; #[tokio::test] -async fn new_identity() { +async fn correct_credentials() { // Set up the environment let app = fixtures::scratch_app().await; + let (name, password) = fixtures::login::create_with_password(&app, &fixtures::now()).await; // Call the endpoint let identity = fixtures::identity::not_logged_in(); let logged_in_at = fixtures::now(); - let (name, password) = fixtures::login::propose(); let request = routes::LoginRequest { name: name.clone(), password, @@ -33,54 +33,42 @@ async fn new_identity() { // Verify the semantics let validated_at = fixtures::now(); - let (_, validated) = app + let (_, validated_login) = app .tokens() .validate(&secret, &validated_at) .await .expect("identity secret is valid"); - assert_eq!(name, validated.name); + assert_eq!(name, validated_login.name); } #[tokio::test] -async fn existing_identity() { +async fn invalid_name() { // Set up the environment let app = fixtures::scratch_app().await; - let (name, password) = fixtures::login::create_with_password(&app, &fixtures::now()).await; // Call the endpoint let identity = fixtures::identity::not_logged_in(); let logged_in_at = fixtures::now(); + let (name, password) = fixtures::login::propose(); let request = routes::LoginRequest { name: name.clone(), password, }; - let (identity, status) = + let routes::LoginError(error) = routes::on_login(State(app.clone()), logged_in_at, identity, Json(request)) .await - .expect("logged in with valid credentials"); + .expect_err("logged in with an incorrect password"); // Verify the return value's basic structure - assert_eq!(StatusCode::NO_CONTENT, status); - let secret = identity.secret().expect("logged in with valid credentials"); - - // Verify the semantics - - let validated_at = fixtures::now(); - let (_, validated_login) = app - .tokens() - .validate(&secret, &validated_at) - .await - .expect("identity secret is valid"); - - assert_eq!(name, validated_login.name); + assert!(matches!(error, app::LoginError::Rejected)); } #[tokio::test] -async fn authentication_failed() { +async fn incorrect_password() { // Set up the environment let app = fixtures::scratch_app().await; diff --git a/src/token/app.rs b/src/token/app.rs index b8af637..04f8747 100644 --- a/src/token/app.rs +++ b/src/token/app.rs @@ -12,27 +12,17 @@ use super::{ use crate::{ clock::DateTime, db::NotFound as _, - event::{self, repo::Provider as _, Event as ServiceEvent}, - login::{repo::Provider as _, Login, Password}, + login::{Login, Password}, }; pub struct Tokens<'a> { db: &'a SqlitePool, - events: &'a event::Broadcaster, token_events: &'a Broadcaster, } impl<'a> Tokens<'a> { - pub const fn new( - db: &'a SqlitePool, - events: &'a event::Broadcaster, - token_events: &'a Broadcaster, - ) -> Self { - Self { - db, - events, - token_events, - } + pub const fn new(db: &'a SqlitePool, token_events: &'a Broadcaster) -> Self { + Self { db, token_events } } pub async fn login( @@ -42,31 +32,21 @@ impl<'a> Tokens<'a> { login_at: &DateTime, ) -> Result { let mut tx = self.db.begin().await?; + let (login, stored_hash) = tx + .auth() + .for_name(name) + .await + .optional()? + .ok_or(LoginError::Rejected)?; - let (login, created) = if let Some((login, stored_hash)) = tx.auth().for_name(name).await? { - if stored_hash.verify(password)? { - // Password verified, proceed with login - (login, false) - } else { - // Password NOT verified. - return Err(LoginError::Rejected); - } + let token = if stored_hash.verify(password)? { + tx.tokens().issue(&login, login_at).await? } else { - let password_hash = password.hash()?; - let created = tx.sequence().next(login_at).await?; - let login = tx.logins().create(name, &password_hash, &created).await?; - - (login, true) + Err(LoginError::Rejected)? }; - let token = tx.tokens().issue(&login, login_at).await?; tx.commit().await?; - if created { - self.events - .broadcast(login.events().map(ServiceEvent::from).collect::>()); - } - Ok(token) } diff --git a/src/token/repo/auth.rs b/src/token/repo/auth.rs index ddb5136..9aee81f 100644 --- a/src/token/repo/auth.rs +++ b/src/token/repo/auth.rs @@ -19,13 +19,7 @@ impl<'c> Provider for Transaction<'c, Sqlite> { pub struct Auth<'t>(&'t mut SqliteConnection); impl<'t> Auth<'t> { - // Retrieves a login by name, plus its stored password hash for - // verification. If there's no login with the requested name, this will - // return [None]. - pub async fn for_name( - &mut self, - name: &str, - ) -> Result, sqlx::Error> { + pub async fn for_name(&mut self, name: &str) -> Result<(History, StoredHash), sqlx::Error> { let found = sqlx::query!( r#" select @@ -54,7 +48,7 @@ impl<'t> Auth<'t> { row.password_hash, ) }) - .fetch_optional(&mut *self.0) + .fetch_one(&mut *self.0) .await?; Ok(found) -- cgit v1.2.3