Skip to content

Commit

Permalink
Ch10: Ensure invalid logins fail
Browse files Browse the repository at this point in the history
  • Loading branch information
tahaafzal5 committed Aug 25, 2024
1 parent ca75fc1 commit 00c25a9
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 12 deletions.
4 changes: 4 additions & 0 deletions Notes/Ch6-10.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@
- [Redirect On Success](#redirect-on-success)
- [Processing Form Data](#processing-form-data)
- [Building An `authentication` Module](#building-an-authentication-module)
- [Rejecting Invalid Credentials](#rejecting-invalid-credentials)

# Ch 6 - Reject Invalid Subscribers #1
* Our input validation for `/POST` is limited: we just ensure that both the `name` and the `email` fields are provided, even if they are empty.
Expand Down Expand Up @@ -1189,3 +1190,6 @@ TEST_LOG=true cargo test --quiet --release newsletters_are_delivered | grep "VER

#### Building An `authentication` Module
* Since `validate_credentials` is polluted with the concerns of `publish_newsletter`, we will create a shared authentication module to reuse in both `POST /login` and `POST /newsletter`.

#### Rejecting Invalid Credentials
* We now start using the authentication module for our login, so any invalid login should now fail.
4 changes: 2 additions & 2 deletions src/authentication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub enum AuthError {
}

pub struct Credentials {
pub username: String,
pub email: String,
pub password: Secret<String>,
}

Expand All @@ -31,7 +31,7 @@ pub async fn validate_credentials(
);

if let Some((stored_user_id, stored_password_hash)) =
get_stored_credentials(&credentials.username, connection_pool)
get_stored_credentials(&credentials.email, connection_pool)
.await
.map_err(AuthError::UnexpectedError)?
{
Expand Down
65 changes: 57 additions & 8 deletions src/routes/login_form/post.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,66 @@
use actix_web::{web, HttpResponse};
use reqwest::header::LOCATION;
use actix_web::{web, HttpResponse, ResponseError};
use reqwest::{header::LOCATION, StatusCode};
use secrecy::Secret;
use sqlx::PgPool;

use crate::routes::home_route;
use crate::{
authentication::{validate_credentials, AuthError, Credentials},
routes::{error_chain_fmt, home_route},
};

#[derive(serde::Deserialize)]
pub struct FormData {
_email: String,
_password: Secret<String>,
email: String,
password: Secret<String>,
}

pub async fn login(_form: web::Form<FormData>) -> HttpResponse {
HttpResponse::SeeOther()
#[derive(thiserror::Error)]
pub enum LoginError {
#[error("Authentication failed")]
AuthError(#[source] anyhow::Error),
#[error("Something went wrong")]
UnexpectedError(#[from] anyhow::Error),
}

impl std::fmt::Debug for LoginError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
error_chain_fmt(self, f)
}
}

impl ResponseError for LoginError {
fn status_code(&self) -> reqwest::StatusCode {
match self {
LoginError::AuthError(_) => StatusCode::UNAUTHORIZED,
LoginError::UnexpectedError(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}

#[tracing::instrument(
skip(form, pool),
fields(email=tracing::field::Empty, user_id=tracing::field::Empty)
)]
pub async fn login(
form: web::Form<FormData>,
pool: web::Data<PgPool>,
) -> Result<HttpResponse, LoginError> {
println!("login called");
let credentials = Credentials {
email: form.0.email,
password: form.0.password,
};
tracing::Span::current().record("email", &tracing::field::display(&credentials.email));

let user_id = validate_credentials(credentials, &pool)
.await
.map_err(|e| match e {
AuthError::InvalidCredentials(_) => LoginError::AuthError(e.into()),
AuthError::UnexpectedError(_) => LoginError::UnexpectedError(e.into()),
})?;
tracing::Span::current().record("user_id", &tracing::field::display(user_id));

Ok(HttpResponse::SeeOther()
.insert_header((LOCATION, home_route()))
.finish()
.finish())
}
4 changes: 2 additions & 2 deletions src/routes/newsletter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ fn basic_authentication(headers: &HeaderMap) -> Result<Credentials, anyhow::Erro
.to_string();

Ok(Credentials {
username,
email: username,
password: Secret::new(password),
})
}
Expand All @@ -118,7 +118,7 @@ pub async fn publish_newsletter(
request: HttpRequest,
) -> Result<HttpResponse, PublishError> {
let credentials = basic_authentication(request.headers()).map_err(PublishError::AuthError)?;
tracing::Span::current().record("username", tracing::field::display(&credentials.username));
tracing::Span::current().record("username", tracing::field::display(&credentials.email));

let user_id = validate_credentials(credentials, &connection_pool)
.await
Expand Down

0 comments on commit 00c25a9

Please sign in to comment.