Utoipa adds auth requirements

This commit is contained in:
Antoine Gersant 2025-01-15 19:53:55 -08:00
parent 2d92ac03ef
commit 9707f4a96d
3 changed files with 182 additions and 6 deletions
src/server

View file

@ -7,7 +7,6 @@ use tower_http::{
normalize_path::{NormalizePath, NormalizePathLayer}, normalize_path::{NormalizePath, NormalizePathLayer},
services::ServeDir, services::ServeDir,
}; };
use utoipa::OpenApi;
use utoipa_axum::router::OpenApiRouter; use utoipa_axum::router::OpenApiRouter;
use utoipa_scalar::{Scalar, Servable}; 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)) .fallback_service(ServeDir::new(&app.web_dir_path))
.layer(CompressionLayer::new()); .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()) .nest("/api", api::router())
.split_for_parts(); .split_for_parts();

View file

@ -113,6 +113,10 @@ async fn get_initial_setup(
get, get,
path = "/settings", path = "/settings",
tag = "Configuration", tag = "Configuration",
security(
("auth_token" = []),
("auth_query_param" = []),
),
responses( responses(
(status = 200, body = dto::Settings), (status = 200, body = dto::Settings),
), ),
@ -141,6 +145,10 @@ async fn get_settings(
put, put,
path = "/settings", path = "/settings",
tag = "Configuration", tag = "Configuration",
security(
("auth_token" = []),
("auth_query_param" = []),
),
request_body = dto::NewSettings, request_body = dto::NewSettings,
)] )]
async fn put_settings( async fn put_settings(
@ -172,6 +180,10 @@ async fn put_settings(
get, get,
path = "/mount_dirs", path = "/mount_dirs",
tag = "Configuration", tag = "Configuration",
security(
("auth_token" = []),
("auth_query_param" = []),
),
responses( responses(
(status = 200, body = Vec<dto::MountDir>), (status = 200, body = Vec<dto::MountDir>),
), ),
@ -189,6 +201,10 @@ async fn get_mount_dirs(
put, put,
path = "/mount_dirs", path = "/mount_dirs",
tag = "Configuration", tag = "Configuration",
security(
("auth_token" = []),
("auth_query_param" = []),
),
request_body = Vec<dto::MountDir>, request_body = Vec<dto::MountDir>,
)] )]
async fn put_mount_dirs( async fn put_mount_dirs(
@ -236,6 +252,10 @@ async fn post_auth(
get, get,
path = "/users", path = "/users",
tag = "User Management", tag = "User Management",
security(
("auth_token" = []),
("auth_query_param" = []),
),
responses( responses(
(status = 200, body = Vec<dto::User>), (status = 200, body = Vec<dto::User>),
), ),
@ -253,6 +273,10 @@ async fn get_users(
post, post,
path = "/user", path = "/user",
tag = "User Management", tag = "User Management",
security(
("auth_token" = []),
("auth_query_param" = []),
),
request_body = dto::NewUser, request_body = dto::NewUser,
responses( responses(
(status = 200), (status = 200),
@ -275,6 +299,10 @@ async fn post_user(
put, put,
path = "/user/{name}", path = "/user/{name}",
tag = "User Management", tag = "User Management",
security(
("auth_token" = []),
("auth_query_param" = []),
),
request_body = dto::UserUpdate, request_body = dto::UserUpdate,
responses( responses(
(status = 200), (status = 200),
@ -309,6 +337,10 @@ async fn put_user(
delete, delete,
path = "/user/{name}", path = "/user/{name}",
tag = "User Management", tag = "User Management",
security(
("auth_token" = []),
("auth_query_param" = []),
),
responses( responses(
(status = 200), (status = 200),
(status = 404), (status = 404),
@ -329,7 +361,15 @@ async fn delete_user(
Ok(()) 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( async fn post_trigger_index(
_admin_rights: AdminRights, _admin_rights: AdminRights,
State(scanner): State<scanner::Scanner>, State(scanner): State<scanner::Scanner>,
@ -342,6 +382,10 @@ async fn post_trigger_index(
get, get,
path = "/index_status", path = "/index_status",
tag = "Configuration", tag = "Configuration",
security(
("auth_token" = []),
("auth_query_param" = []),
),
responses( responses(
(status = 200, body = dto::IndexStatus), (status = 200, body = dto::IndexStatus),
) )
@ -423,6 +467,10 @@ fn albums_to_response(albums: Vec<index::Album>, api_version: APIMajorVersion) -
get, get,
path = "/browse", path = "/browse",
tag = "File Browser", tag = "File Browser",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params( params(
("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8) ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8)
), ),
@ -446,6 +494,10 @@ async fn get_browse_root(
get, get,
path = "/browse/{*path}", path = "/browse/{*path}",
tag = "File Browser", tag = "File Browser",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params( params(
("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8), ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8),
("path", allow_reserved), ("path", allow_reserved),
@ -471,6 +523,10 @@ async fn get_browse(
get, get,
path = "/flatten", path = "/flatten",
tag = "File Browser", tag = "File Browser",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params( params(
("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8), ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8),
), ),
@ -495,6 +551,10 @@ async fn get_flatten_root(
get, get,
path = "/flatten/{*path}", path = "/flatten/{*path}",
tag = "File Browser", tag = "File Browser",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params( params(
("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8), ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8),
("path", allow_reserved), ("path", allow_reserved),
@ -521,6 +581,10 @@ async fn get_flatten(
get, get,
path = "/albums", path = "/albums",
tag = "Collection", tag = "Collection",
security(
("auth_token" = []),
("auth_query_param" = []),
),
responses( responses(
(status = 200, body = Vec<dto::AlbumHeader>), (status = 200, body = Vec<dto::AlbumHeader>),
) )
@ -544,6 +608,10 @@ async fn get_albums(
get, get,
path = "/artists", path = "/artists",
tag = "Collection", tag = "Collection",
security(
("auth_token" = []),
("auth_query_param" = []),
),
responses( responses(
(status = 200, body = Vec<dto::ArtistHeader>), (status = 200, body = Vec<dto::ArtistHeader>),
) )
@ -567,6 +635,10 @@ async fn get_artists(
get, get,
path = "/artists/{artist}", path = "/artists/{artist}",
tag = "Collection", tag = "Collection",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params(("artist",)), params(("artist",)),
responses( responses(
(status = 200, body = dto::Artist), (status = 200, body = dto::Artist),
@ -584,6 +656,10 @@ async fn get_artist(
get, get,
path = "/artists/{artists}/albums/{album}", path = "/artists/{artists}/albums/{album}",
tag = "Collection", tag = "Collection",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params( params(
("artists",), ("artists",),
("album",), ("album",),
@ -608,6 +684,10 @@ async fn get_album(
post, // post because of https://github.com/whatwg/fetch/issues/551 post, // post because of https://github.com/whatwg/fetch/issues/551
path = "/songs", path = "/songs",
tag = "Collection", tag = "Collection",
security(
("auth_token" = []),
("auth_query_param" = []),
),
request_body = dto::GetSongsBulkInput, request_body = dto::GetSongsBulkInput,
responses( responses(
(status = 200, body = dto::GetSongsBulkOutput), (status = 200, body = dto::GetSongsBulkOutput),
@ -639,6 +719,10 @@ async fn get_songs(
get, get,
path = "/peaks/{*path}", path = "/peaks/{*path}",
tag = "Media", tag = "Media",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params(("path", allow_reserved)), params(("path", allow_reserved)),
responses( responses(
(status = 200, body = [u8]), (status = 200, body = [u8]),
@ -659,6 +743,10 @@ async fn get_peaks(
get, get,
path = "/albums/random", path = "/albums/random",
tag = "Collection", tag = "Collection",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params( params(
("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8), ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8),
dto::GetRandomAlbumsParameters, dto::GetRandomAlbumsParameters,
@ -689,6 +777,10 @@ async fn get_random_albums(
get, get,
path = "/albums/recent", path = "/albums/recent",
tag = "Collection", tag = "Collection",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params( params(
("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8), ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8),
dto::GetRecentAlbumsParameters dto::GetRecentAlbumsParameters
@ -716,6 +808,10 @@ async fn get_recent_albums(
get, get,
path = "/genres", path = "/genres",
tag = "Collection", tag = "Collection",
security(
("auth_token" = []),
("auth_query_param" = []),
),
responses( responses(
(status = 200, body = Vec<dto::GenreHeader>), (status = 200, body = Vec<dto::GenreHeader>),
) )
@ -738,6 +834,10 @@ async fn get_genres(
get, get,
path = "/genres/{genre}", path = "/genres/{genre}",
tag = "Collection", tag = "Collection",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params(("genre",)), params(("genre",)),
responses( responses(
(status = 200, body = Vec<dto::Genre>), (status = 200, body = Vec<dto::Genre>),
@ -755,6 +855,10 @@ async fn get_genre(
get, get,
path = "/genres/{genre}/albums", path = "/genres/{genre}/albums",
tag = "Collection", tag = "Collection",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params(("genre",)), params(("genre",)),
responses( responses(
(status = 200, body = Vec<dto::AlbumHeader>), (status = 200, body = Vec<dto::AlbumHeader>),
@ -779,6 +883,10 @@ async fn get_genre_albums(
get, get,
path = "/genres/{genre}/artists", path = "/genres/{genre}/artists",
tag = "Collection", tag = "Collection",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params(("genre",)), params(("genre",)),
responses( responses(
(status = 200, body = Vec<dto::ArtistHeader>), (status = 200, body = Vec<dto::ArtistHeader>),
@ -803,6 +911,10 @@ async fn get_genre_artists(
get, get,
path = "/genres/{genre}/songs", path = "/genres/{genre}/songs",
tag = "Collection", tag = "Collection",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params(("genre",)), params(("genre",)),
responses( responses(
(status = 200, body = dto::SongList), (status = 200, body = dto::SongList),
@ -829,6 +941,10 @@ async fn get_genre_songs(
get, get,
path = "/search/{*query}", path = "/search/{*query}",
tag = "Collection", tag = "Collection",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params( params(
("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8), ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8),
("query", allow_reserved), ("query", allow_reserved),
@ -874,6 +990,10 @@ async fn get_search(
get, get,
path = "/playlists", path = "/playlists",
tag = "Playlists", tag = "Playlists",
security(
("auth_token" = []),
("auth_query_param" = []),
),
responses( responses(
(status = 200, body = Vec<dto::PlaylistHeader>), (status = 200, body = Vec<dto::PlaylistHeader>),
) )
@ -892,6 +1012,10 @@ async fn get_playlists(
put, put,
path = "/playlist/{name}", path = "/playlist/{name}",
tag = "Playlists", tag = "Playlists",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params(("name",)), params(("name",)),
request_body = dto::SavePlaylistInput, request_body = dto::SavePlaylistInput,
)] )]
@ -918,6 +1042,10 @@ async fn put_playlist(
get, get,
path = "/playlist/{name}", path = "/playlist/{name}",
tag = "Playlists", tag = "Playlists",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params( params(
("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8), ("Accept-Version" = Option<i32>, Header, minimum = 7, maximum = 8),
("name",), ("name",),
@ -955,6 +1083,10 @@ async fn get_playlist(
delete, delete,
path = "/playlist/{name}", path = "/playlist/{name}",
tag = "Playlists", tag = "Playlists",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params(("name",)), params(("name",)),
)] )]
async fn delete_playlist( async fn delete_playlist(
@ -972,6 +1104,10 @@ async fn delete_playlist(
get, get,
path = "/audio/{*path}", path = "/audio/{*path}",
tag = "Media", tag = "Media",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params(("path", allow_reserved)), params(("path", allow_reserved)),
responses( responses(
(status = 206, body = [u8]), (status = 206, body = [u8]),
@ -1002,6 +1138,10 @@ async fn get_audio(
get, get,
path = "/thumbnail/{*path}", path = "/thumbnail/{*path}",
tag = "Media", tag = "Media",
security(
("auth_token" = []),
("auth_query_param" = []),
),
params( params(
("path", allow_reserved), ("path", allow_reserved),
dto::ThumbnailOptions dto::ThumbnailOptions

View file

@ -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 fn open_api() -> OpenApi {
pub struct ApiDoc; 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()
}