Skip to content

Commit

Permalink
chapter 10 part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
josemoura212 committed Jun 26, 2024
1 parent 69e01e5 commit ea6b146
Show file tree
Hide file tree
Showing 12 changed files with 157 additions and 6 deletions.
7 changes: 7 additions & 0 deletions src/authentication.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#[derive(thiserror::Error, Debug)]
pub enum AuthError {
#[error("Invalid credentials.")]
InvalidCredentials(#[source] anyhow::Error),
#[error(transparent)]
UnexpectedError(#[from] anyhow::Error),
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod authentication;
pub mod configuration;
pub mod domain;
pub mod email_client;
Expand Down
11 changes: 11 additions & 0 deletions src/routes/home/home.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Home</title>
</head>

<body>
<p>Welcome to our newsletter!</p>
</body>
</html>
7 changes: 7 additions & 0 deletions src/routes/home/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use actix_web::{http::header::ContentType, HttpResponse};

pub async fn home() -> HttpResponse {
HttpResponse::Ok()
.content_type(ContentType::html())
.body(include_str!("home.html"))
}
7 changes: 7 additions & 0 deletions src/routes/login/get.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use actix_web::{http::header::ContentType, HttpResponse};

pub async fn login_form() -> HttpResponse {
HttpResponse::Ok()
.content_type(ContentType::html())
.body(include_str!("login.html"))
}
21 changes: 21 additions & 0 deletions src/routes/login/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Login</title>
</head>
<body>
<form action="/login" method="post">
<label
>Username
<input type="text" placeholder="Enter Username" name="username" />
</label>
<label
>Password
<input type="password" placeholder="Enter Password" name="password" />
</label>

<button type="submit">Login</button>
</form>
</body>
</html>
5 changes: 5 additions & 0 deletions src/routes/login/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod get;
mod post;

pub use get::login_form;
pub use post::login;
17 changes: 17 additions & 0 deletions src/routes/login/post.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use actix_web::http::header::LOCATION;
use actix_web::web;
use actix_web::HttpResponse;
use secrecy::Secret;

#[allow(unused)]
#[derive(serde::Deserialize)]
pub struct FormData {
username: String,
password: Secret<String>,
}

pub async fn login(_form: web::Form<FormData>) -> HttpResponse {
HttpResponse::SeeOther()
.insert_header((LOCATION, "/"))
.finish()
}
4 changes: 4 additions & 0 deletions src/routes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
mod health_check;
mod home;
mod login;
mod newsletters;
mod subscriptions;
mod subscriptions_confirm;

pub use health_check::*;
pub use home::*;
pub use login::*;
pub use newsletters::*;
pub use subscriptions::*;
pub use subscriptions_confirm::*;
7 changes: 6 additions & 1 deletion src/startup.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::configuration::{DatabaseSettings, Settings};
use crate::email_client::EmailClient;
use crate::routes::{confirm, health_check, publish_newsletter, subscribe};
use crate::routes::{
confirm, health_check, home, login, login_form, publish_newsletter, subscribe,
};
use actix_web::web::Data;
use actix_web::{dev::Server, web, App, HttpServer};
use sqlx::postgres::PgPoolOptions;
Expand Down Expand Up @@ -77,6 +79,9 @@ fn run(
.route("/subscriptions", web::post().to(subscribe))
.route("/subscriptions/confirm", web::get().to(confirm))
.route("/newsletters", web::post().to(publish_newsletter))
.route("/login", web::get().to(login_form))
.route("/login", web::post().to(login))
.route("/", web::get().to(home))
.app_data(db_pool.clone())
.app_data(email_client.clone())
.app_data(base_url.clone())
Expand Down
14 changes: 9 additions & 5 deletions tests/api/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use argon2::{
password_hash::{PasswordHasher, SaltString},
Argon2,
Argon2, Params,
};
use once_cell::sync::Lazy;
use sqlx::{Connection, Executor, PgConnection, PgPool};
Expand Down Expand Up @@ -52,10 +52,14 @@ impl TestUser {

async fn store(&self, pool: &PgPool) {
let salt = SaltString::generate(&mut rand::thread_rng());
let password_hash = Argon2::default()
.hash_password(self.password.as_bytes(), &salt)
.unwrap()
.to_string();
let password_hash = Argon2::new(
argon2::Algorithm::Argon2id,
argon2::Version::V0x13,
Params::new(15000, 2, 1, None).unwrap(),
)
.hash_password(self.password.as_bytes(), &salt)
.unwrap()
.to_string();

sqlx::query!(
r#"
Expand Down
62 changes: 62 additions & 0 deletions tests/api/newsletter.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::helpers::{spawn_app, ConfirmationLinks, TestApp};
use uuid::Uuid;
use wiremock::matchers::{any, method, path};
use wiremock::{Mock, ResponseTemplate};

Expand Down Expand Up @@ -148,3 +149,64 @@ async fn requests_missing_authorization_are_rejected() {
response.headers()["WWW-Authenticate"]
);
}

#[tokio::test]
async fn non_existing_user_is_rejected() {
// Arrange
let app = spawn_app().await;
// Random credentials
let username = Uuid::new_v4().to_string();
let password = Uuid::new_v4().to_string();

let response = reqwest::Client::new()
.post(&format!("{}/newsletters", &app.address))
.basic_auth(username, Some(password))
.json(&serde_json::json!({
"title": "Newsletter title",
"content": {
"text": "Newsletter body as plain text",
"html": "<p>Newsletter body as HTML</p>",
}
}))
.send()
.await
.expect("Failed to execute request.");

// Assert
assert_eq!(401, response.status().as_u16());
assert_eq!(
r#"Basic realm="publish""#,
response.headers()["WWW-Authenticate"]
);
}

#[tokio::test]
async fn invalid_password_is_rejected() {
// Arrange
let app = spawn_app().await;
let username = &app.test_user.username;
// Random password
let password = Uuid::new_v4().to_string();
assert_ne!(app.test_user.password, password);

let response = reqwest::Client::new()
.post(&format!("{}/newsletters", &app.address))
.basic_auth(username, Some(password))
.json(&serde_json::json!({
"title": "Newsletter title",
"content": {
"text": "Newsletter body as plain text",
"html": "<p>Newsletter body as HTML</p>",
}
}))
.send()
.await
.expect("Failed to execute request.");

// Assert
assert_eq!(401, response.status().as_u16());
assert_eq!(
r#"Basic realm="publish""#,
response.headers()["WWW-Authenticate"]
);
}

0 comments on commit ea6b146

Please sign in to comment.