diff --git a/src/apis/routes.rs b/src/apis/routes.rs index 305ecefc..b9d0603b 100644 --- a/src/apis/routes.rs +++ b/src/apis/routes.rs @@ -137,6 +137,13 @@ pub async fn delete_auth_key_handler(State(tracker): State>, Path(s } } +pub async fn reload_keys_handler(State(tracker): State>) -> Response { + match tracker.load_keys().await { + Ok(..) => response_ok(), + Err(..) => response_err("failed to reload keys".to_string()), + } +} + /// Serde deserialization decorator to map empty Strings to None, fn empty_string_as_none<'de, D, T>(de: D) -> Result, D::Error> where diff --git a/src/apis/server.rs b/src/apis/server.rs index 1184908d..d046f171 100644 --- a/src/apis/server.rs +++ b/src/apis/server.rs @@ -12,7 +12,7 @@ use warp::hyper; use super::middlewares::auth::auth; use super::routes::{ add_torrent_to_whitelist_handler, delete_auth_key_handler, delete_torrent_from_whitelist_handler, generate_auth_key_handler, - get_stats_handler, get_torrent_handler, get_torrents_handler, reload_whitelist_handler, + get_stats_handler, get_torrent_handler, get_torrents_handler, reload_keys_handler, reload_whitelist_handler, }; use crate::tracker; @@ -46,6 +46,8 @@ pub fn start(socket_addr: SocketAddr, tracker: &Arc) -> impl F .delete(delete_auth_key_handler) .with_state(tracker.clone()), ) + // Key command + .route("/keys/reload", get(reload_keys_handler).with_state(tracker.clone())) .layer(middleware::from_fn_with_state(tracker.config.clone(), auth)); let server = axum::Server::bind(&socket_addr).serve(app.into_make_service()); @@ -90,6 +92,8 @@ pub fn start_tls( .delete(delete_auth_key_handler) .with_state(tracker.clone()), ) + // Key command + .route("/keys/reload", get(reload_keys_handler).with_state(tracker.clone())) .layer(middleware::from_fn_with_state(tracker.config.clone(), auth)); let handle = Handle::new(); diff --git a/tests/tracker_api.rs b/tests/tracker_api.rs index 30c4fa9d..46a11b48 100644 --- a/tests/tracker_api.rs +++ b/tests/tracker_api.rs @@ -1131,7 +1131,8 @@ mod tracker_apis { use torrust_tracker::tracker::auth::Key; use crate::api::asserts::{ - assert_failed_to_delete_key, assert_failed_to_generate_key, assert_ok, assert_token_not_valid, assert_unauthorized, + assert_failed_to_delete_key, assert_failed_to_generate_key, assert_failed_to_reload_keys, assert_ok, + assert_token_not_valid, assert_unauthorized, }; use crate::api::client::Client; use crate::api::connection_info::{connection_with_invalid_token, connection_with_no_token}; @@ -1209,7 +1210,7 @@ mod tracker_apis { #[tokio::test] async fn should_return_an_error_when_the_auth_key_cannot_be_deleted() { - let api_server = start_default_api(&Version::Warp).await; + let api_server = start_default_api(&Version::Axum).await; let seconds_valid = 60; let auth_key = api_server @@ -1220,7 +1221,7 @@ mod tracker_apis { force_database_error(&api_server.tracker); - let response = Client::new(api_server.get_connection_info(), &Version::Warp) + let response = Client::new(api_server.get_connection_info(), &Version::Axum) .delete_auth_key(&auth_key.key) .await; @@ -1229,7 +1230,7 @@ mod tracker_apis { #[tokio::test] async fn should_not_allow_deleting_an_auth_key_for_unauthenticated_users() { - let api_server = start_default_api(&Version::Warp).await; + let api_server = start_default_api(&Version::Axum).await; let seconds_valid = 60; @@ -1240,7 +1241,7 @@ mod tracker_apis { .await .unwrap(); - let response = Client::new(connection_with_invalid_token(&api_server.get_bind_address()), &Version::Warp) + let response = Client::new(connection_with_invalid_token(&api_server.get_bind_address()), &Version::Axum) .delete_auth_key(&auth_key.key) .await; @@ -1253,11 +1254,73 @@ mod tracker_apis { .await .unwrap(); - let response = Client::new(connection_with_no_token(&api_server.get_bind_address()), &Version::Warp) + let response = Client::new(connection_with_no_token(&api_server.get_bind_address()), &Version::Axum) .delete_auth_key(&auth_key.key) .await; assert_unauthorized(response).await; } + + #[tokio::test] + async fn should_allow_reloading_keys() { + let api_server = start_default_api(&Version::Axum).await; + + let seconds_valid = 60; + api_server + .tracker + .generate_auth_key(Duration::from_secs(seconds_valid)) + .await + .unwrap(); + + let response = Client::new(api_server.get_connection_info(), &Version::Axum) + .reload_keys() + .await; + + assert_eq!(response.status(), 200); + } + + #[tokio::test] + async fn should_return_an_error_when_keys_cannot_be_reloaded() { + let api_server = start_default_api(&Version::Axum).await; + + let seconds_valid = 60; + api_server + .tracker + .generate_auth_key(Duration::from_secs(seconds_valid)) + .await + .unwrap(); + + force_database_error(&api_server.tracker); + + let response = Client::new(api_server.get_connection_info(), &Version::Axum) + .reload_keys() + .await; + + assert_failed_to_reload_keys(response).await; + } + + #[tokio::test] + async fn should_not_allow_reloading_keys_for_unauthenticated_users() { + let api_server = start_default_api(&Version::Axum).await; + + let seconds_valid = 60; + api_server + .tracker + .generate_auth_key(Duration::from_secs(seconds_valid)) + .await + .unwrap(); + + let response = Client::new(connection_with_invalid_token(&api_server.get_bind_address()), &Version::Axum) + .reload_keys() + .await; + + assert_token_not_valid(response).await; + + let response = Client::new(connection_with_no_token(&api_server.get_bind_address()), &Version::Axum) + .reload_keys() + .await; + + assert_unauthorized(response).await; + } } }