Skip to content

Commit 45e3547

Browse files
committed
add a test for #89
1 parent 39453c7 commit 45e3547

File tree

4 files changed

+45
-23
lines changed

4 files changed

+45
-23
lines changed

src/filesystem.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ impl FileSystem {
9595
for (i, component) in path.components().enumerate() {
9696
if let Component::Normal(c) = component {
9797
if !priviledged && i == 0 && c.eq_ignore_ascii_case("sqlpage") {
98-
anyhow::bail!("Access to the sqlpage config directory is not allowed.");
98+
anyhow::bail!(ErrorWithStatus {
99+
status: actix_web::http::StatusCode::FORBIDDEN,
100+
});
99101
}
100102
} else {
101103
anyhow::bail!(

src/webserver/error_with_status.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use actix_web::http::StatusCode;
1+
use actix_web::{http::StatusCode, ResponseError};
22

33
#[derive(Debug, PartialEq)]
44
pub struct ErrorWithStatus {
@@ -10,3 +10,12 @@ impl std::fmt::Display for ErrorWithStatus {
1010
}
1111
}
1212
impl std::error::Error for ErrorWithStatus {}
13+
14+
impl ResponseError for ErrorWithStatus {
15+
fn status_code(&self) -> StatusCode {
16+
self.status
17+
}
18+
fn error_response(&self) -> actix_web::HttpResponse {
19+
actix_web::HttpResponse::build(self.status).body(self.status.to_string())
20+
}
21+
}

src/webserver/http.rs

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::webserver::database::{stream_query_results, DbItem};
33
use crate::webserver::ErrorWithStatus;
44
use crate::{AppState, Config, ParsedSqlFile};
55
use actix_web::dev::{fn_service, ServiceFactory, ServiceRequest};
6-
use actix_web::error::{ErrorInternalServerError, ErrorNotFound};
6+
use actix_web::error::ErrorInternalServerError;
77
use actix_web::http::header::{ContentType, Header, HttpDate, IfModifiedSince, LastModified};
88
use actix_web::http::{header, StatusCode, Uri};
99
use actix_web::web::Form;
@@ -411,24 +411,21 @@ async fn process_sql_request(
411411
.sql_file_cache
412412
.get(app_state, &sql_path)
413413
.await
414-
.map_err(|e| {
415-
log::error!("Error while trying to get SQL file: {:#}", e);
416-
if e.downcast_ref()
417-
== Some(&ErrorWithStatus {
418-
status: StatusCode::NOT_FOUND,
419-
})
420-
{
421-
ErrorNotFound("The requested file was not found.")
422-
} else {
423-
ErrorInternalServerError(format!(
424-
"An error occurred while trying to handle your request: {e:#}"
425-
))
426-
}
427-
})?;
414+
.map_err(anyhow_err_to_actix)?;
428415
let response = render_sql(&mut req, sql_file).await?;
429416
Ok(req.into_response(response))
430417
}
431418

419+
fn anyhow_err_to_actix(e: anyhow::Error) -> actix_web::Error {
420+
log::error!("Error while trying to get SQL file: {:#}", e);
421+
match e.downcast::<ErrorWithStatus>() {
422+
Ok(err) => actix_web::Error::from(err),
423+
Err(e) => ErrorInternalServerError(format!(
424+
"An error occurred while trying to handle your request: {e:#}"
425+
)),
426+
}
427+
}
428+
432429
async fn serve_file(
433430
path: &str,
434431
state: &AppState,
@@ -441,7 +438,7 @@ async fn serve_file(
441438
.file_system
442439
.modified_since(state, path.as_ref(), since, false)
443440
.await
444-
.map_err(actix_web::error::ErrorBadRequest)?;
441+
.map_err(anyhow_err_to_actix)?;
445442
if !modified {
446443
return Ok(HttpResponse::NotModified().finish());
447444
}
@@ -450,7 +447,7 @@ async fn serve_file(
450447
.file_system
451448
.read_file(state, path.as_ref(), false)
452449
.await
453-
.map_err(actix_web::error::ErrorBadRequest)
450+
.map_err(anyhow_err_to_actix)
454451
.map(|b| {
455452
HttpResponse::Ok()
456453
.insert_header(

tests/index.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use actix_web::{
2+
body::MessageBody,
23
http::{self, header::ContentType},
34
test,
45
};
56
use sqlpage::{app_config::AppConfig, webserver::http::main_handler, AppState};
67

78
#[actix_web::test]
89
async fn test_index_ok() {
9-
let resp = req_path("/").await;
10+
let resp = req_path("/").await.unwrap();
1011
assert_eq!(resp.status(), http::StatusCode::OK);
1112
let body = test::read_body(resp).await;
1213
assert!(body.starts_with(b"<!DOCTYPE html>"));
@@ -16,7 +17,20 @@ async fn test_index_ok() {
1617
assert!(!body.contains("error"));
1718
}
1819

19-
async fn req_path(path: &str) -> actix_web::dev::ServiceResponse {
20+
#[actix_web::test]
21+
async fn test_access_config_forbidden() {
22+
let resp_result = req_path("/sqlpage/sqlpage.json").await;
23+
assert!(resp_result.is_err(), "Accessing the config file should be forbidden, but we received a response: {resp_result:?}");
24+
let resp = resp_result.unwrap_err().error_response();
25+
assert_eq!(resp.status(), http::StatusCode::FORBIDDEN);
26+
assert!(
27+
String::from_utf8_lossy(&resp.into_body().try_into_bytes().unwrap())
28+
.to_lowercase()
29+
.contains("forbidden"),
30+
);
31+
}
32+
33+
async fn req_path(path: &str) -> Result<actix_web::dev::ServiceResponse, actix_web::Error> {
2034
init_log();
2135
let config = test_config();
2236
let state = AppState::init(&config).await.unwrap();
@@ -26,7 +40,7 @@ async fn req_path(path: &str) -> actix_web::dev::ServiceResponse {
2640
.app_data(data)
2741
.insert_header(ContentType::plaintext())
2842
.to_srv_request();
29-
main_handler(req).await.unwrap()
43+
main_handler(req).await
3044
}
3145

3246
pub fn test_config() -> AppConfig {
@@ -44,5 +58,5 @@ pub fn test_config() -> AppConfig {
4458
}
4559

4660
fn init_log() {
47-
env_logger::builder().is_test(true).try_init().unwrap();
61+
let _ = env_logger::builder().is_test(true).try_init();
4862
}

0 commit comments

Comments
 (0)