-
Notifications
You must be signed in to change notification settings - Fork 0
finish: chapter 10 #21
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,11 +1,15 @@ | ||||||||||||||||||||||||||||||||||||||||||
| use axum::{http::StatusCode, response::IntoResponse}; | ||||||||||||||||||||||||||||||||||||||||||
| use axum::{ | ||||||||||||||||||||||||||||||||||||||||||
| body::Body, | ||||||||||||||||||||||||||||||||||||||||||
| http::StatusCode, | ||||||||||||||||||||||||||||||||||||||||||
| response::{IntoResponse, Response}, | ||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| pub type Result<T = ()> = std::result::Result<T, Error>; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| #[derive(thiserror::Error, Debug)] | ||||||||||||||||||||||||||||||||||||||||||
| pub enum Error { | ||||||||||||||||||||||||||||||||||||||||||
| #[error(transparent)] | ||||||||||||||||||||||||||||||||||||||||||
| SurrealDb(Box<surrealdb::Error>), | ||||||||||||||||||||||||||||||||||||||||||
| SurrealDb(#[from] Box<surrealdb::Error>), | ||||||||||||||||||||||||||||||||||||||||||
| #[error("{0:?}")] | ||||||||||||||||||||||||||||||||||||||||||
| Migrations(String), | ||||||||||||||||||||||||||||||||||||||||||
| #[error(transparent)] | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -20,6 +24,10 @@ pub enum Error { | |||||||||||||||||||||||||||||||||||||||||
| ValidationError(#[from] validator::ValidationError), | ||||||||||||||||||||||||||||||||||||||||||
| #[error(transparent)] | ||||||||||||||||||||||||||||||||||||||||||
| Reqwest(#[from] reqwest::Error), | ||||||||||||||||||||||||||||||||||||||||||
| #[error(transparent)] | ||||||||||||||||||||||||||||||||||||||||||
| Session(#[from] tower_sessions::session::Error), | ||||||||||||||||||||||||||||||||||||||||||
| #[error("{0:?}")] | ||||||||||||||||||||||||||||||||||||||||||
| Auth(String), | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| #[error("{0:?}")] | ||||||||||||||||||||||||||||||||||||||||||
| Custom(String), | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -32,12 +40,20 @@ impl From<surrealdb::Error> for Error { | |||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| impl IntoResponse for Error { | ||||||||||||||||||||||||||||||||||||||||||
| fn into_response(self) -> axum::response::Response { | ||||||||||||||||||||||||||||||||||||||||||
| fn into_response(self) -> Response { | ||||||||||||||||||||||||||||||||||||||||||
| match self { | ||||||||||||||||||||||||||||||||||||||||||
| Self::ValidationErrors(_) | Self::ValidationError(_) => { | ||||||||||||||||||||||||||||||||||||||||||
| tracing::info!("Bad request: - {self:?}"); | ||||||||||||||||||||||||||||||||||||||||||
| tracing::warn!("Bad request: - {self:?}"); | ||||||||||||||||||||||||||||||||||||||||||
| StatusCode::BAD_REQUEST.into_response() | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| Self::Auth(_) => { | ||||||||||||||||||||||||||||||||||||||||||
| tracing::warn!("Unauthorized : - {self:?}"); | ||||||||||||||||||||||||||||||||||||||||||
| Response::builder() | ||||||||||||||||||||||||||||||||||||||||||
| .status(StatusCode::UNAUTHORIZED) | ||||||||||||||||||||||||||||||||||||||||||
| .header("WWW-Authenticate", r#"Basic realm="publish""#) | ||||||||||||||||||||||||||||||||||||||||||
| .body(Body::empty()) | ||||||||||||||||||||||||||||||||||||||||||
| .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR.into_response()) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+49
to
+56
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid logging secrets; add charset to WWW-Authenticate. Do not log Apply: - Self::Auth(_) => {
- tracing::warn!("Unauthorized : - {self:?}");
- Response::builder()
- .status(StatusCode::UNAUTHORIZED)
- .header("WWW-Authenticate", r#"Basic realm="publish""#)
- .body(Body::empty())
- .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR.into_response())
- }
+ Self::Auth(_) => {
+ // Avoid logging credential material.
+ tracing::warn!("Unauthorized");
+ Response::builder()
+ .status(StatusCode::UNAUTHORIZED)
+ .header(axum::http::header::WWW_AUTHENTICATE, r#"Basic realm="publish", charset="UTF-8""#)
+ .body(Body::empty())
+ .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR.into_response())
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
| _ => { | ||||||||||||||||||||||||||||||||||||||||||
| tracing::error!("Internal Server: - {self:?}"); | ||||||||||||||||||||||||||||||||||||||||||
| StatusCode::INTERNAL_SERVER_ERROR.into_response() | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| use crate::{Result, model::ModelManager, session_state::TypedSession}; | ||
| use axum::{ | ||
| extract::State, | ||
| response::{Html, IntoResponse, Redirect}, | ||
| }; | ||
| use reqwest::StatusCode; | ||
| use std::sync::Arc; | ||
|
|
||
| pub async fn admin_dashboard( | ||
| State(mm): State<Arc<ModelManager>>, | ||
| session: TypedSession, | ||
| ) -> Result<impl IntoResponse> { | ||
| let username = match session.get_user_id().await { | ||
| Ok(Some(user_id)) => mm.get_username(user_id).await?, | ||
| reason => { | ||
| tracing::error!("Failed to authenticate: {reason:?}"); | ||
| return Ok(Redirect::to("/login").into_response()); | ||
| } | ||
| }; | ||
|
Comment on lines
+13
to
+19
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Escape
use std::sync::Arc;
+use askama_escape::escape;
@@
};
+ let username = escape(&username).to_string();
+
let body = format!(
r#"
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Admin dashboard</title>
</head>
<body>
<p>Welcome {username}</p>
</body>
</html>
"#
);Add dependency (Cargo.toml): askama_escape = "0.10"Also applies to: 21-36 🤖 Prompt for AI Agents |
||
|
|
||
| let body = format!( | ||
| r#" | ||
| <!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <meta http-equiv="content-type" content="text/html; charset=utf-8"> | ||
| <title>Admin dashboard</title> | ||
| </head> | ||
| <body> | ||
| <p>Welcome {username}</p> | ||
| </body> | ||
| </html> | ||
| "# | ||
| ); | ||
|
|
||
| Ok((StatusCode::OK, Html(body)).into_response()) | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,10 @@ | ||||||
| <!doctype html> | ||||||
| <html lang="en"> | ||||||
| <head> | ||||||
| <meta http-equiv="content-type" content="text/html; charset=utf-8" /> | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Use modern charset meta Prefer the shorter, HTML5-standard - <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <meta charset="utf-8">📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| <title>Home</title> | ||||||
| </head> | ||||||
| <body> | ||||||
| <p>Welcome to our newsletter!</p> | ||||||
| </body> | ||||||
| </html> | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| use crate::Result; | ||
| use axum::response::{Html, IntoResponse}; | ||
| use reqwest::StatusCode; | ||
|
|
||
| pub async fn home() -> Result<impl IntoResponse> { | ||
| Ok((StatusCode::OK, Html(include_str!("home.html")))) | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,49 @@ | ||||||||
| use crate::Result; | ||||||||
| use axum::response::{Html, IntoResponse}; | ||||||||
| use axum_messages::Messages; | ||||||||
| use reqwest::StatusCode; | ||||||||
|
|
||||||||
| pub async fn login( | ||||||||
| messages: Messages, | ||||||||
| // Query(query): Option<Query<QueryParams>>, | ||||||||
| ) -> Result<impl IntoResponse> { | ||||||||
|
Comment on lines
+8
to
+9
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Remove dead, commented parameter. - // Query(query): Option<Query<QueryParams>>,📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||
| let error_message = messages | ||||||||
| .into_iter() | ||||||||
| .map(|message| format!("<p><i>{}</i></p>", message.message)) | ||||||||
| .collect::<Vec<_>>() | ||||||||
| .join(""); | ||||||||
|
|
||||||||
|
Comment on lines
+10
to
+15
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Escape flash messages to prevent HTML injection.
use axum_messages::Messages;
use reqwest::StatusCode;
+use askama_escape::escape;
@@
- let error_message = messages
+ let error_message = messages
.into_iter()
- .map(|message| format!("<p><i>{}</i></p>", message.message))
+ .map(|message| format!("<p><i>{}</i></p>", escape(&message.message)))
.collect::<Vec<_>>()
.join("");Add dependency (Cargo.toml): askama_escape = "0.10"Also applies to: 16-46 🤖 Prompt for AI Agents |
||||||||
| let body = format!( | ||||||||
| r#" | ||||||||
| <!DOCTYPE html> | ||||||||
| <html lang="en"> | ||||||||
| <head> | ||||||||
| <meta http-equiv="content-type" content="text/html; charset=utf-8"> | ||||||||
| <title>Login</title> | ||||||||
| </head> | ||||||||
| <body> | ||||||||
| {error_message} | ||||||||
| <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> | ||||||||
| "# | ||||||||
| ); | ||||||||
|
|
||||||||
| Ok((StatusCode::OK, Html(body))) | ||||||||
| } | ||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Version compatibility and toolchain pinning.
The added auth/session deps look consistent, but please sanity-check cross-crate compatibility (axum 0.8.x, tower-http 0.6.x, tower-sessions 0.14.x, store 0.7.x) and ensure CI uses a toolchain that supports
edition = "2024".rust-versionto Cargo.toml and pinning toolchain viarust-toolchain.toml.🏁 Script executed:
Length of output: 650
🌐 Web query:
💡 Result:
Short answer: probably not without changes.
Recommendation: if you need to run tower-sessions 0.14 + SurrealDB 2.x + axum 0.8, either
References:
If you want, I can check the exact tower-sessions-surrealdb-store Cargo.toml and open-source code to list the specific API mismatches and a minimal patch.
🏁 Script executed:
Length of output: 576
Patch session store compatibility & confirm toolchain pinning
rust-version = "1.89"to Cargo.toml.base64 = "^0.22") to allow automatic patch upgrades.