Skip to content

Commit 5bdf98c

Browse files
committed
add a new "readirect" head component, which redirects the user and then stops the execution (contrarily to the existing "http_header" component)
1 parent e91ad60 commit 5bdf98c

File tree

2 files changed

+46
-0
lines changed

2 files changed

+46
-0
lines changed

src/render.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@ use std::borrow::Cow;
1111
use std::sync::Arc;
1212

1313
pub enum PageContext<W: std::io::Write> {
14+
/// Indicates that we should stay in the header context
1415
Header(HeaderContext<W>),
16+
17+
/// Indicates that we should start rendering the body
1518
Body {
1619
http_response: HttpResponseBuilder,
1720
renderer: RenderContext<W>,
1821
},
22+
23+
/// The headers have been set, but no response body should be sent
24+
Close(HeaderContext<W>),
1925
}
2026

2127
/// Handles the first SQL statements, before the headers have been sent to
@@ -42,7 +48,9 @@ impl<W: std::io::Write> HeaderContext<W> {
4248
match get_object_str(&data, "component") {
4349
Some("status_code") => self.status_code(&data).map(PageContext::Header),
4450
Some("http_header") => self.add_http_header(&data).map(PageContext::Header),
51+
Some("redirect") => self.redirect(&data).map(PageContext::Close),
4552
Some("cookie") => self.add_cookie(&data).map(PageContext::Header),
53+
Some("authentication") => self.authentication(&data),
4654
_ => self.start_body(data).await,
4755
}
4856
}
@@ -123,6 +131,40 @@ impl<W: std::io::Write> HeaderContext<W> {
123131
Ok(self)
124132
}
125133

134+
fn redirect(mut self, data: &JsonValue) -> anyhow::Result<Self> {
135+
self.response.status(StatusCode::FOUND);
136+
self.has_status = true;
137+
let link = get_object_str(data, "link")
138+
.with_context(|| "The redirect component requires a 'link' property")?;
139+
self.response.insert_header((header::LOCATION, link));
140+
Ok(self)
141+
}
142+
143+
fn authentication(mut self, data: &JsonValue) -> anyhow::Result<PageContext<W>> {
144+
use argon2::Argon2;
145+
use password_hash::PasswordHash;
146+
let link = get_object_str(data, "link")
147+
.with_context(|| "The authentication component requires a 'link' property")?;
148+
let password_hash = get_object_str(data, "password_hash");
149+
let password = get_object_str(data, "password");
150+
if let (Some(password), Some(password_hash)) = (password, password_hash) {
151+
match PasswordHash::new(password_hash)
152+
.map_err(|e| {
153+
anyhow::anyhow!("invalid value for the password_hash property: {}", e)
154+
})?
155+
.verify_password(&[&Argon2::default()], password)
156+
{
157+
Ok(()) => return Ok(PageContext::Header(self)),
158+
Err(e) => log::info!("User authentication failed: {}", e),
159+
}
160+
}
161+
// The authentication failed
162+
self.response.status(StatusCode::FOUND);
163+
self.response.insert_header((header::LOCATION, link));
164+
self.has_status = true;
165+
Ok(PageContext::Close(self))
166+
}
167+
126168
async fn start_body(self, data: JsonValue) -> anyhow::Result<PageContext<W>> {
127169
let renderer = RenderContext::new(self.app_state, self.writer, data)
128170
.await

src/webserver/http.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ async fn build_response_header_and_stream<S: Stream<Item = DbItem>>(
171171
database_entries_stream: stream,
172172
});
173173
}
174+
PageContext::Close(h) => {
175+
head_context = h;
176+
break;
177+
}
174178
}
175179
}
176180
DbItem::FinishedQuery => {

0 commit comments

Comments
 (0)