diff --git a/src/server/axum.rs b/src/server/axum.rs index d402a7c..4371c72 100644 --- a/src/server/axum.rs +++ b/src/server/axum.rs @@ -7,7 +7,6 @@ use tower_http::{ normalize_path::{NormalizePath, NormalizePathLayer}, services::ServeDir, }; -use utoipa::OpenApi; use utoipa_axum::router::OpenApiRouter; use utoipa_scalar::{Scalar, Servable}; @@ -25,7 +24,7 @@ pub fn make_router(app: App) -> NormalizePath<Router> { .fallback_service(ServeDir::new(&app.web_dir_path)) .layer(CompressionLayer::new()); - let (open_api_router, open_api) = OpenApiRouter::with_openapi(doc::ApiDoc::openapi()) + let (open_api_router, open_api) = OpenApiRouter::with_openapi(doc::open_api()) .nest("/api", api::router()) .split_for_parts(); diff --git a/src/server/axum/api.rs b/src/server/axum/api.rs index 86abcae..42939ef 100644 --- a/src/server/axum/api.rs +++ b/src/server/axum/api.rs @@ -113,6 +113,10 @@ async fn get_initial_setup( get, path = "/settings", tag = "Configuration", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), responses( (status = 200, body = dto::Settings), ), @@ -141,6 +145,10 @@ async fn get_settings( put, path = "/settings", tag = "Configuration", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), request_body = dto::NewSettings, )] async fn put_settings( @@ -172,6 +180,10 @@ async fn put_settings( get, path = "/mount_dirs", tag = "Configuration", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), responses( (status = 200, body = Vec<dto::MountDir>), ), @@ -189,6 +201,10 @@ async fn get_mount_dirs( put, path = "/mount_dirs", tag = "Configuration", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), request_body = Vec<dto::MountDir>, )] async fn put_mount_dirs( @@ -236,6 +252,10 @@ async fn post_auth( get, path = "/users", tag = "User Management", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), responses( (status = 200, body = Vec<dto::User>), ), @@ -253,6 +273,10 @@ async fn get_users( post, path = "/user", tag = "User Management", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), request_body = dto::NewUser, responses( (status = 200), @@ -275,6 +299,10 @@ async fn post_user( put, path = "/user/{name}", tag = "User Management", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), request_body = dto::UserUpdate, responses( (status = 200), @@ -309,6 +337,10 @@ async fn put_user( delete, path = "/user/{name}", tag = "User Management", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), responses( (status = 200), (status = 404), @@ -329,7 +361,15 @@ async fn delete_user( Ok(()) } -#[utoipa::path(post, path = "/trigger_index", tag = "Configuration")] +#[utoipa::path( + post, + path = "/trigger_index", + tag = "Configuration", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), +)] async fn post_trigger_index( _admin_rights: AdminRights, State(scanner): State<scanner::Scanner>, @@ -342,6 +382,10 @@ async fn post_trigger_index( get, path = "/index_status", tag = "Configuration", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), responses( (status = 200, body = dto::IndexStatus), ) @@ -423,6 +467,10 @@ fn albums_to_response(albums: Vec<index::Album>, api_version: APIMajorVersion) - get, path = "/browse", tag = "File Browser", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params( ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8) ), @@ -446,6 +494,10 @@ async fn get_browse_root( get, path = "/browse/{*path}", tag = "File Browser", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params( ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8), ("path", allow_reserved), @@ -471,6 +523,10 @@ async fn get_browse( get, path = "/flatten", tag = "File Browser", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params( ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8), ), @@ -495,6 +551,10 @@ async fn get_flatten_root( get, path = "/flatten/{*path}", tag = "File Browser", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params( ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8), ("path", allow_reserved), @@ -521,6 +581,10 @@ async fn get_flatten( get, path = "/albums", tag = "Collection", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), responses( (status = 200, body = Vec<dto::AlbumHeader>), ) @@ -544,6 +608,10 @@ async fn get_albums( get, path = "/artists", tag = "Collection", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), responses( (status = 200, body = Vec<dto::ArtistHeader>), ) @@ -567,6 +635,10 @@ async fn get_artists( get, path = "/artists/{artist}", tag = "Collection", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params(("artist",)), responses( (status = 200, body = dto::Artist), @@ -584,6 +656,10 @@ async fn get_artist( get, path = "/artists/{artists}/albums/{album}", tag = "Collection", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params( ("artists",), ("album",), @@ -608,6 +684,10 @@ async fn get_album( post, // post because of https://github.com/whatwg/fetch/issues/551 path = "/songs", tag = "Collection", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), request_body = dto::GetSongsBulkInput, responses( (status = 200, body = dto::GetSongsBulkOutput), @@ -639,6 +719,10 @@ async fn get_songs( get, path = "/peaks/{*path}", tag = "Media", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params(("path", allow_reserved)), responses( (status = 200, body = [u8]), @@ -659,6 +743,10 @@ async fn get_peaks( get, path = "/albums/random", tag = "Collection", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params( ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8), dto::GetRandomAlbumsParameters, @@ -689,6 +777,10 @@ async fn get_random_albums( get, path = "/albums/recent", tag = "Collection", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params( ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8), dto::GetRecentAlbumsParameters @@ -716,6 +808,10 @@ async fn get_recent_albums( get, path = "/genres", tag = "Collection", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), responses( (status = 200, body = Vec<dto::GenreHeader>), ) @@ -738,6 +834,10 @@ async fn get_genres( get, path = "/genres/{genre}", tag = "Collection", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params(("genre",)), responses( (status = 200, body = Vec<dto::Genre>), @@ -755,6 +855,10 @@ async fn get_genre( get, path = "/genres/{genre}/albums", tag = "Collection", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params(("genre",)), responses( (status = 200, body = Vec<dto::AlbumHeader>), @@ -779,6 +883,10 @@ async fn get_genre_albums( get, path = "/genres/{genre}/artists", tag = "Collection", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params(("genre",)), responses( (status = 200, body = Vec<dto::ArtistHeader>), @@ -803,6 +911,10 @@ async fn get_genre_artists( get, path = "/genres/{genre}/songs", tag = "Collection", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params(("genre",)), responses( (status = 200, body = dto::SongList), @@ -829,6 +941,10 @@ async fn get_genre_songs( get, path = "/search/{*query}", tag = "Collection", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params( ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8), ("query", allow_reserved), @@ -874,6 +990,10 @@ async fn get_search( get, path = "/playlists", tag = "Playlists", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), responses( (status = 200, body = Vec<dto::PlaylistHeader>), ) @@ -892,6 +1012,10 @@ async fn get_playlists( put, path = "/playlist/{name}", tag = "Playlists", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params(("name",)), request_body = dto::SavePlaylistInput, )] @@ -918,6 +1042,10 @@ async fn put_playlist( get, path = "/playlist/{name}", tag = "Playlists", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params( ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8), ("name",), @@ -955,6 +1083,10 @@ async fn get_playlist( delete, path = "/playlist/{name}", tag = "Playlists", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params(("name",)), )] async fn delete_playlist( @@ -972,6 +1104,10 @@ async fn delete_playlist( get, path = "/audio/{*path}", tag = "Media", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params(("path", allow_reserved)), responses( (status = 206, body = [u8]), @@ -1002,6 +1138,10 @@ async fn get_audio( get, path = "/thumbnail/{*path}", tag = "Media", + security( + ("auth_token" = []), + ("auth_query_param" = []), + ), params( ("path", allow_reserved), dto::ThumbnailOptions diff --git a/src/server/doc.rs b/src/server/doc.rs index 3af22a9..add86b0 100644 --- a/src/server/doc.rs +++ b/src/server/doc.rs @@ -1,4 +1,41 @@ -use utoipa::OpenApi; +use utoipa::openapi::{ + security::{ApiKey, ApiKeyValue, HttpAuthScheme, HttpBuilder, SecurityScheme}, + ComponentsBuilder, ContactBuilder, InfoBuilder, License, OpenApi, OpenApiBuilder, +}; -#[derive(OpenApi)] -pub struct ApiDoc; +pub fn open_api() -> OpenApi { + let auth_token_description = "Authentication token acquired from the `/auth` endpoint"; + + OpenApiBuilder::new() + .info( + InfoBuilder::new() + .title(env!("CARGO_PKG_NAME")) + .version(env!("CARGO_PKG_VERSION")) + .license(Some(License::new("MIT"))) + .contact(Some( + ContactBuilder::new().name(Some("Antoine Gersant")).build(), + )) + .build(), + ) + .components(Some( + ComponentsBuilder::new() + .security_scheme( + "auth_header", + SecurityScheme::Http( + HttpBuilder::new() + .scheme(HttpAuthScheme::Bearer) + .description(Some(auth_token_description)) + .build(), + ), + ) + .security_scheme( + "auth_query_param", + SecurityScheme::ApiKey(ApiKey::Query(ApiKeyValue::with_description( + "auth_token", + auth_token_description, + ))), + ) + .build(), + )) + .build() +}