diff --git a/config.toml b/config.toml index cdf3297..b04fb78 100644 --- a/config.toml +++ b/config.toml @@ -7,6 +7,7 @@ address="127.0.0.1:8000" max_content_length="10MB" upload_path="./upload" timeout="30s" +expose_version=true landing_page="""Submit files via HTTP POST here: curl -F 'file=@example.txt' " This will return the finished URL. diff --git a/src/config.rs b/src/config.rs index 2c76767..ca0aec0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -43,6 +43,8 @@ pub struct ServerConfig { pub auth_token: Option, /// Landing page text. pub landing_page: Option, + /// Expose version. + pub expose_version: Option, } /// Paste configuration. diff --git a/src/server.rs b/src/server.rs index 8054d6e..eab55a6 100644 --- a/src/server.rs +++ b/src/server.rs @@ -88,6 +88,32 @@ async fn serve( } } +/// Expose version endpoint +#[get("/version")] +async fn version( + request: HttpRequest, + config: web::Data>, +) -> Result { + let config = config + .read() + .map_err(|_| error::ErrorInternalServerError("cannot acquire config"))?; + let connection = request.connection_info().clone(); + let host = connection.peer_addr().unwrap_or("unknown host"); + auth::check( + host, + request.headers(), + env::var(AUTH_TOKEN_ENV) + .ok() + .or_else(|| config.server.auth_token.as_ref().cloned()), + )?; + if !config.server.expose_version.unwrap_or(false) { + log::warn!("server is not configured to expose version endpoint"); + Err(error::ErrorForbidden("endpoint is not exposed"))?; + } + let version = env!("CARGO_PKG_VERSION"); + Ok(HttpResponse::Ok().body(version)) +} + /// Handles file upload by processing `multipart/form-data`. #[post("/")] async fn upload( @@ -205,6 +231,7 @@ async fn upload( /// Configures the server routes. pub fn configure_routes(cfg: &mut web::ServiceConfig) { cfg.service(index) + .service(version) .service(serve) .service(upload) .route("", web::head().to(HttpResponse::MethodNotAllowed)); @@ -299,6 +326,70 @@ mod tests { Ok(()) } + #[actix_web::test] + async fn test_version_without_auth() -> Result<(), Error> { + let mut config = Config::default(); + config.server.auth_token = Some(String::from("test")); + let app = test::init_service( + App::new() + .app_data(Data::new(RwLock::new(config))) + .app_data(Data::new(Client::default())) + .configure(configure_routes), + ) + .await; + + let request = TestRequest::default() + .insert_header(("content-type", "text/plain")) + .uri("/version") + .to_request(); + let response = test::call_service(&app, request).await; + assert_eq!(StatusCode::UNAUTHORIZED, response.status()); + assert_body(response, "unauthorized").await?; + Ok(()) + } + + #[actix_web::test] + async fn test_version_without_config() -> Result<(), Error> { + let app = test::init_service( + App::new() + .app_data(Data::new(RwLock::new(Config::default()))) + .app_data(Data::new(Client::default())) + .configure(configure_routes), + ) + .await; + + let request = TestRequest::default() + .insert_header(("content-type", "text/plain")) + .uri("/version") + .to_request(); + let response = test::call_service(&app, request).await; + assert_eq!(StatusCode::FORBIDDEN, response.status()); + assert_body(response, "endpoint is not exposed").await?; + Ok(()) + } + + #[actix_web::test] + async fn test_version() -> Result<(), Error> { + let mut config = Config::default(); + config.server.expose_version = Some(true); + let app = test::init_service( + App::new() + .app_data(Data::new(RwLock::new(config))) + .app_data(Data::new(Client::default())) + .configure(configure_routes), + ) + .await; + + let request = TestRequest::default() + .insert_header(("content-type", "text/plain")) + .uri("/version") + .to_request(); + let response = test::call_service(&app, request).await; + assert_eq!(StatusCode::OK, response.status()); + assert_body(response, env!("CARGO_PKG_VERSION")).await?; + Ok(()) + } + #[actix_web::test] async fn test_auth() -> Result<(), Error> { let mut config = Config::default();