feat(server): expose version as endpoint (#29)

Fixes: #27

Signed-off-by: Leonidas Spyropoulos <artafinde@archlinux.org>
This commit is contained in:
Leonidas Spyropoulos 2022-10-04 10:38:37 +01:00 committed by GitHub
parent 00cee6d9ba
commit 5cdbc8da61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 94 additions and 0 deletions

View File

@ -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' <server>"
This will return the finished URL.

View File

@ -43,6 +43,8 @@ pub struct ServerConfig {
pub auth_token: Option<String>,
/// Landing page text.
pub landing_page: Option<String>,
/// Expose version.
pub expose_version: Option<bool>,
}
/// Paste configuration.

View File

@ -88,6 +88,32 @@ async fn serve(
}
}
/// Expose version endpoint
#[get("/version")]
async fn version(
request: HttpRequest,
config: web::Data<RwLock<Config>>,
) -> Result<HttpResponse, Error> {
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();