Skip to content

Commit 172e0b2

Browse files
committed
Visual html errors for 403 and 429
Refactor HTTP unauthorized response Use a builder to set the content type and WWW-Authenticate header on the unauthorized response. Also, remove unnecessary downcast and improve the handling of `sqlx::Error::PoolTimedOut`.
1 parent ab2e068 commit 172e0b2

File tree

3 files changed

+28
-24
lines changed

3 files changed

+28
-24
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- fixed decoding of UUID values
55
- Fixed handling of NULL values in `sqlpage.link`. They were encoded as the string `'null'` instead of being omitted from the link's parameters.
66
- Enable submenu autoclosing (on click) in the shell. This is not ideal, but this prevents a bug introduced in v0.36.0 where the page would scroll back to the top when clicking anywhere on the page after navigating from a submenu. The next version will fix this properly. See https://github.com/sqlpage/SQLPage/issues/1011
7+
- Adopt the new nice visual errors introduced in v0.37.1 for "403 Forbidden" and "429 Too Many Requests" errors.
78

89
## v0.37.0
910
- We now cryptographically sign the Windows app during releases, which proves the file hasn’t been tampered with. Once the production certificate is active, Windows will show a "verified publisher" and should stop showing screens saying "This app might harm your device", "Windows protected your PC" or "Are you sure you want to run this application ?".

src/render.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ use crate::webserver::ErrorWithStatus;
4848
use crate::AppState;
4949
use actix_web::cookie::time::format_description::well_known::Rfc3339;
5050
use actix_web::cookie::time::OffsetDateTime;
51+
use actix_web::http::header::ContentType;
5152
use actix_web::http::{header, StatusCode};
5253
use actix_web::{HttpResponse, HttpResponseBuilder, ResponseError};
5354
use anyhow::{bail, format_err, Context as AnyhowContext};
@@ -323,10 +324,15 @@ impl HeaderContext {
323324
Redirecting to the login page...",
324325
)
325326
} else {
326-
ErrorWithStatus {
327-
status: StatusCode::UNAUTHORIZED,
328-
}
329-
.error_response()
327+
let mut resp_builder = actix_web::HttpResponse::build(StatusCode::UNAUTHORIZED);
328+
resp_builder.content_type(ContentType::plaintext());
329+
resp_builder.insert_header((
330+
header::WWW_AUTHENTICATE,
331+
header::HeaderValue::from_static(
332+
"Basic realm=\"Authentication required\", charset=\"UTF-8\"",
333+
),
334+
));
335+
resp_builder.body("Sorry, but you are not authorized to access this page.")
330336
};
331337
self.has_status = true;
332338
Ok(PageContext::Close(http_response))

src/webserver/error.rs

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -56,32 +56,29 @@ fn anyhow_err_to_actix_resp(e: &anyhow::Error, state: &AppState) -> HttpResponse
5656
let mut resp = HttpResponseBuilder::new(StatusCode::INTERNAL_SERVER_ERROR);
5757
resp.insert_header((header::CONTENT_TYPE, header::ContentType::plaintext()));
5858

59-
if let Some(status_err @ &ErrorWithStatus { .. }) = e.downcast_ref() {
60-
status_err.error_response()
59+
if let Some(&ErrorWithStatus { status }) = e.downcast_ref() {
60+
resp.status(status);
6161
} else if let Some(sqlx::Error::PoolTimedOut) = e.downcast_ref() {
6262
use rand::Rng;
63-
resp.status(StatusCode::TOO_MANY_REQUESTS)
64-
.insert_header((
65-
header::RETRY_AFTER,
66-
header::HeaderValue::from(rand::rng().random_range(1..=15)),
67-
))
68-
.body("The database is currently too busy to handle your request. Please try again later.".to_owned())
69-
} else {
70-
match error_to_html_string(state, e) {
71-
Ok(body) => {
72-
resp.insert_header((header::CONTENT_TYPE, header::ContentType::html()));
73-
resp.body(body)
74-
}
75-
Err(second_err) => {
76-
log::error!("Unable to render error: {e:#}");
77-
resp.body(format!(
78-
"A second error occurred while rendering the error page: \n\n\
63+
resp.status(StatusCode::TOO_MANY_REQUESTS).insert_header((
64+
header::RETRY_AFTER,
65+
header::HeaderValue::from(rand::rng().random_range(1..=15)),
66+
));
67+
}
68+
match error_to_html_string(state, e) {
69+
Ok(body) => {
70+
resp.insert_header((header::CONTENT_TYPE, header::ContentType::html()));
71+
resp.body(body)
72+
}
73+
Err(second_err) => {
74+
log::error!("Unable to render error: {e:#}");
75+
resp.body(format!(
76+
"A second error occurred while rendering the error page: \n\n\
7977
Initial error: \n\
8078
{e:#}\n\n\
8179
Second error: \n\
8280
{second_err:#}"
83-
))
84-
}
81+
))
8582
}
8683
}
8784
}

0 commit comments

Comments
 (0)