diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 20cb1bd..711b72c 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -68,7 +68,9 @@ jobs: env: ROOT_DIRECTORY: "/tmp" DATABASE_URL: "postgres://${{ secrets.POSTGRES_USER }}:${{ secrets.POSTGRES_PASSWORD }}@localhost:5432/${{ secrets.POSTGRES_DB }}" - run: cargo test --verbose -- + run: | + cat .env.sample | head -1 > .env + cargo test --verbose -- - name: Run clippy run: cargo clippy -- -D warnings diff --git a/Cargo.lock b/Cargo.lock index 0ede540..2c870c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,12 +17,27 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + [[package]] name = "atoi" version = "2.0.0" @@ -93,6 +108,29 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-extra" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bf463831f5131b7d3c756525b305d40f1185b688565648a92e1392ca35713d" +dependencies = [ + "axum", + "axum-core", + "bytes", + "cookie", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "serde", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.75" @@ -108,6 +146,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -222,6 +266,17 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -588,8 +643,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -762,19 +819,24 @@ dependencies = [ [[package]] name = "icarus" -version = "0.1.100" +version = "0.1.101" dependencies = [ "axum", + "axum-extra", + "base64 0.21.7", "common-multipart-rfc7578", "futures", "icarus_envy", "icarus_meta", "icarus_models", + "josekit", + "jsonwebtoken", "mime_guess", "serde", "serde_json", "sqlx", "tempfile", + "thiserror 1.0.69", "time", "tokio", "tokio-util", @@ -804,9 +866,10 @@ dependencies = [ [[package]] name = "icarus_models" -version = "0.5.1" -source = "git+ssh://git@git.kundeng.us/phoenix/icarus_models.git?tag=v0.5.1-devel-1c5de9dc26-111#1c5de9dc26099ac5f21e1dc7a1f7d0dbb892b34b" +version = "0.5.2" +source = "git+ssh://git@git.kundeng.us/phoenix/icarus_models.git?tag=v0.5.2-devel-d3251f935e-111#d3251f935e76caa110727203d910ee0683a668a0" dependencies = [ + "josekit", "rand 0.9.1", "serde", "serde_json", @@ -937,6 +1000,23 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "josekit" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a808e078330e6af222eb0044b71d4b1ff981bfef43e7bc8133a88234e0c86a0c" +dependencies = [ + "anyhow", + "base64 0.22.1", + "flate2", + "openssl", + "regex", + "serde", + "serde_json", + "thiserror 2.0.12", + "time", +] + [[package]] name = "js-sys" version = "0.3.77" @@ -947,6 +1027,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64 0.22.1", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1134,6 +1229,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -1296,6 +1401,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pem" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" +dependencies = [ + "base64 0.22.1", + "serde", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -1466,6 +1581,49 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rsa" version = "0.9.8" @@ -1581,6 +1739,7 @@ version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ + "indexmap", "itoa", "memchr", "ryu", @@ -1665,6 +1824,18 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simple_asn1" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.12", + "time", +] + [[package]] name = "slab" version = "0.4.10" @@ -1728,7 +1899,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "crc", "crossbeam-queue", @@ -1804,7 +1975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bitflags", "byteorder", "bytes", @@ -1848,7 +2019,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bitflags", "byteorder", "crc", @@ -2279,6 +2450,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.4" diff --git a/Cargo.toml b/Cargo.toml index a8715fb..d4f288e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,25 +1,30 @@ [package] name = "icarus" -version = "0.1.100" +version = "0.1.101" edition = "2024" rust-version = "1.88" [dependencies] axum = { version = "0.8.4", features = ["multipart"] } +axum-extra = { version = "0.10.1", features = ["cookie"] } serde = { version = "1.0.219", features = ["derive"] } serde_json = { version = "1.0.140" } tower = { version = "0.5.2", features = ["full"] } tokio = { version = "1.45.1", features = ["full"] } tokio-util = { version = "0.7.15", features = ["io"] } -tower-http = { version = "0.6.6", features = ["timeout"] } +tower-http = { version = "0.6.6", features = ["cors", "timeout"] } tracing-subscriber = "0.3.19" futures = { version = "0.3.31" } mime_guess = { version = "2.0.5" } uuid = { version = "1.17.0", features = ["v4", "serde"] } sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio-native-tls", "time", "uuid"] } time = { version = "0.3.41", features = ["formatting", "macros", "parsing", "serde"] } +thiserror = "1.0" +base64 = "0.21" +jsonwebtoken = { version = "9.3.1" } +josekit = { version = "0.10.1" } icarus_meta = { git = "ssh://git@git.kundeng.us/phoenix/icarus_meta.git", tag = "v0.3.0-devel-f4b71de969-680" } -icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.5.1-devel-1c5de9dc26-111" } +icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.5.2-devel-d3251f935e-111" } icarus_envy = { git = "ssh://git@git.kundeng.us/phoenix/icarus_envy.git", tag = "v0.3.0-devel-d73fba9899-006" } [dev-dependencies] diff --git a/src/auth.rs b/src/auth.rs new file mode 100644 index 0000000..f3bf405 --- /dev/null +++ b/src/auth.rs @@ -0,0 +1,121 @@ +// use std::collections::BTreeMap; +// use std::sync::Arc; + +use axum::{ + Json, + http::{Request, StatusCode}, + middleware::Next, + response::IntoResponse, +}; +use axum_extra::extract::cookie::CookieJar; +use jsonwebtoken::{DecodingKey, Validation, decode}; +use serde::{Deserialize, Serialize}; +// use serde_json::{json, Value}; +use thiserror::Error; +// use time::OffsetDateTime; + +fn deserialize_i64_from_f64<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + let val = f64::deserialize(deserializer)?; + // Handle NaN and infinity cases + if val.is_nan() || val.is_infinite() { + return Err(serde::de::Error::custom("invalid float value")); + } + // Round to nearest integer and convert + let rounded = val.round(); + // Check if the rounded value can fit in i64 + if rounded < (i64::MIN as f64) || rounded > (i64::MAX as f64) { + return Err(serde::de::Error::custom("float out of i64 range")); + } + Ok(rounded as i64) +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct UserClaims { + pub iss: String, + pub aud: String, // Audience + pub sub: String, // Subject (user ID) + #[serde(deserialize_with = "deserialize_i64_from_f64")] + pub exp: i64, // Expiration time (UTC timestamp) + #[serde(deserialize_with = "deserialize_i64_from_f64")] + pub iat: i64, // Issued at (UTC timestamp) + // pub azp: String, + // pub gty: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub roles: Option>, // Optional roles +} + +#[derive(Error, Debug)] +pub enum JwtError { + #[error("Token creation failed")] + TokenCreation, + #[error("Token verification failed")] + TokenVerification, + #[error("Invalid token")] + InvalidToken, + #[error("Token expired")] + ExpiredToken, + #[error("Invalid key")] + InvalidKey, +} + +#[derive(Debug, Serialize)] +pub struct ErrorResponse { + pub status: &'static str, + pub message: String, +} + +pub async fn auth( + cookie_jar: CookieJar, + req: Request, + next: Next, +) -> Result)> { + println!("Cookie: {cookie_jar:?}"); + + let token = cookie_jar + .get("token") + .map(|cookie| cookie.value().to_string()) + .or_else(|| { + req.headers() + .get(axum::http::header::AUTHORIZATION) + .and_then(|auth_header| auth_header.to_str().ok()) + .and_then(|auth_value| { + println!("Auth value: {auth_value:?}"); + auth_value.strip_prefix("Bearer ").map(String::from) + }) + }); + + let token = token.ok_or_else(|| { + let json_error = ErrorResponse { + status: "fail", + message: "You are not logged in, please provide token".to_string(), + }; + (StatusCode::UNAUTHORIZED, Json(json_error)) + })?; + + let secret_key = icarus_envy::environment::get_secret_main_key().await; + + let mut validation = Validation::new(jsonwebtoken::Algorithm::HS256); + validation.set_audience(&["icarus"]); // Must match exactly what's in the token + let claims = decode::( + &token, + // TODO: Replace with code to get secret from env + &DecodingKey::from_secret(secret_key.as_ref()), + &validation, + ) + .map_err(|err| { + eprintln!("Error: {err:?}"); + let json_error = ErrorResponse { + status: "fail", + message: "Invalid token - Error decoding claims".to_string(), + }; + (StatusCode::UNAUTHORIZED, Json(json_error)) + })? + .claims; + + println!("Claims: {claims:?}"); + + Ok(next.run(req).await) +} diff --git a/src/main.rs b/src/main.rs index 85719dd..8207964 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +pub mod auth; pub mod callers; pub mod db { @@ -48,96 +49,146 @@ pub mod init { use std::time::Duration; use tower_http::timeout::TimeoutLayer; + use axum::http::{ + HeaderValue, Method, + header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE}, + }; + pub async fn routes() -> axum::Router { axum::Router::new() .route(crate::ROOT, get(crate::root)) .route( crate::callers::endpoints::QUEUESONG, - post(crate::callers::song::endpoint::queue_song), + post(crate::callers::song::endpoint::queue_song).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::QUEUESONG, - patch(crate::callers::song::endpoint::update_song_queue_status), + patch(crate::callers::song::endpoint::update_song_queue_status).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::QUEUESONGLINKUSERID, - patch(crate::callers::song::endpoint::link_user_id), + patch(crate::callers::song::endpoint::link_user_id).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::QUEUESONGDATA, - get(crate::callers::song::endpoint::download_flac), + get(crate::callers::song::endpoint::download_flac).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::NEXTQUEUESONG, - get(crate::callers::song::endpoint::fetch_queue_song), + get(crate::callers::song::endpoint::fetch_queue_song).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::QUEUESONGUPDATE, - patch(crate::callers::song::endpoint::update_song_queue), + patch(crate::callers::song::endpoint::update_song_queue).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::QUEUESONGDATAWIPE, - patch(crate::callers::song::endpoint::wipe_data_from_song_queue), + patch(crate::callers::song::endpoint::wipe_data_from_song_queue).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::QUEUEMETADATA, - post(crate::callers::metadata::endpoint::queue_metadata), + post(crate::callers::metadata::endpoint::queue_metadata).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::QUEUEMETADATA, - get(crate::callers::metadata::endpoint::fetch_metadata), + get(crate::callers::metadata::endpoint::fetch_metadata).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::QUEUECOVERART, - post(crate::callers::coverart::endpoint::queue), + post(crate::callers::coverart::endpoint::queue).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::QUEUECOVERARTDATA, - get(crate::callers::coverart::endpoint::fetch_coverart_with_data), + get(crate::callers::coverart::endpoint::fetch_coverart_with_data).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::QUEUECOVERART, - get(crate::callers::coverart::endpoint::fetch_coverart_no_data), + get(crate::callers::coverart::endpoint::fetch_coverart_no_data).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::QUEUECOVERARTLINK, - patch(crate::callers::coverart::endpoint::link), + patch(crate::callers::coverart::endpoint::link).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::QUEUECOVERARTDATAWIPE, - patch(crate::callers::coverart::endpoint::wipe_data_from_coverart_queue), + patch(crate::callers::coverart::endpoint::wipe_data_from_coverart_queue) + .route_layer(axum::middleware::from_fn( + crate::auth::auth::, + )), ) .route( crate::callers::endpoints::CREATESONG, - post(crate::callers::song::endpoint::create_metadata), + post(crate::callers::song::endpoint::create_metadata).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::CREATECOVERART, - post(crate::callers::coverart::endpoint::create_coverart), + post(crate::callers::coverart::endpoint::create_coverart).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::GETSONGS, - get(crate::callers::song::endpoint::get_songs), + get(crate::callers::song::endpoint::get_songs).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::GETCOVERART, - get(crate::callers::coverart::endpoint::get_coverart), + get(crate::callers::coverart::endpoint::get_coverart).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::DOWNLOADCOVERART, - get(crate::callers::coverart::endpoint::download_coverart), + get(crate::callers::coverart::endpoint::download_coverart).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::STREAMSONG, - get(crate::callers::song::endpoint::stream_song), + get(crate::callers::song::endpoint::stream_song).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::DOWNLOADSONG, - get(crate::callers::song::endpoint::download_song), + get(crate::callers::song::endpoint::download_song).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) .route( crate::callers::endpoints::DELETESONG, - delete(crate::callers::song::endpoint::delete_song), + delete(crate::callers::song::endpoint::delete_song).route_layer( + axum::middleware::from_fn(crate::auth::auth::), + ), ) } @@ -148,11 +199,18 @@ pub mod init { // TODO: Look into handling this. Seems redundant to run migrations multiple times crate::db::migrations(&pool).await; + let cors = tower_http::cors::CorsLayer::new() + .allow_origin("http://localhost:3000".parse::().unwrap()) + .allow_methods([Method::GET, Method::POST, Method::PATCH, Method::DELETE]) + .allow_credentials(true) + .allow_headers([AUTHORIZATION, ACCEPT, CONTENT_TYPE]); + routes() .await .layer(axum::Extension(pool)) .layer(axum::extract::DefaultBodyLimit::max(1024 * 1024 * 1024)) .layer(TimeoutLayer::new(Duration::from_secs(300))) + .layer(cors) } } @@ -275,6 +333,36 @@ mod tests { } } + pub fn token_fields() -> (String, String, String) { + ( + String::from("What a twist!"), + String::from("icarus_test"), + String::from("icarus"), + ) + } + + pub async fn test_token() -> Result { + let key: String = icarus_envy::environment::get_secret_main_key().await; + let (message, issuer, audience) = token_fields(); + + match icarus_models::token::create_token(&key, &message, &issuer, &audience) { + Ok((access_token, _some_time)) => Ok(access_token), + Err(err) => Err(err), + } + } + + pub async fn bearer_auth() -> String { + let token = match test_token().await { + Ok(access_token) => access_token, + Err(err) => { + assert!(false, "Error: {err:?}"); + String::new() + } + }; + + format!("Bearer {token}") + } + // TODO: Put the *_req() functions in their own module async fn song_queue_req( app: &axum::Router, @@ -290,6 +378,7 @@ mod tests { .method(axum::http::Method::POST) .uri(crate::callers::endpoints::QUEUESONG) .header(axum::http::header::CONTENT_TYPE, content_type) + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from_stream(body)) .unwrap(); app.clone().oneshot(req).await @@ -309,6 +398,7 @@ mod tests { .method(axum::http::Method::PATCH) .uri(crate::callers::endpoints::QUEUESONGLINKUSERID) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from(payload.to_string())) .unwrap(); @@ -322,6 +412,7 @@ mod tests { .method(axum::http::Method::GET) .uri(crate::callers::endpoints::NEXTQUEUESONG) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::empty()) .unwrap(); app.clone().oneshot(fetch_req).await @@ -337,6 +428,7 @@ mod tests { .method(axum::http::Method::GET) .uri(uri) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::empty()) .unwrap(); @@ -355,6 +447,7 @@ mod tests { .method(axum::http::Method::GET) .uri(uri) .header(axum::http::header::CONTENT_TYPE, "audio/flac") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::empty()) .unwrap(); @@ -375,6 +468,7 @@ mod tests { .method(axum::http::Method::POST) .uri(crate::callers::endpoints::QUEUECOVERART) .header(axum::http::header::CONTENT_TYPE, content_type) + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from_stream(body)) .unwrap(); @@ -392,6 +486,7 @@ mod tests { .method(axum::http::Method::POST) .uri(crate::callers::endpoints::QUEUEMETADATA) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from(payload.to_string())) .unwrap(); @@ -412,6 +507,7 @@ mod tests { .method(axum::http::Method::PATCH) .uri(crate::callers::endpoints::QUEUECOVERARTLINK) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from(payload.to_string())) .unwrap(); @@ -431,6 +527,7 @@ mod tests { .method(axum::http::Method::POST) .uri(crate::callers::endpoints::CREATECOVERART) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from(payload.to_string())) .unwrap(); app.clone().oneshot(req).await @@ -447,6 +544,7 @@ mod tests { .method(axum::http::Method::POST) .uri(crate::callers::endpoints::CREATESONG) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from(payload.to_string())) .unwrap(); @@ -466,6 +564,7 @@ mod tests { .method(axum::http::Method::PATCH) .uri(crate::callers::endpoints::QUEUESONG) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from(payload.to_string())) .unwrap(); @@ -486,6 +585,7 @@ mod tests { .method(axum::http::Method::GET) .uri(uri) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::empty()) .unwrap(); @@ -950,6 +1050,10 @@ mod tests { .method(axum::http::Method::PATCH) .uri(uri) .header(axum::http::header::CONTENT_TYPE, content_type) + .header( + axum::http::header::AUTHORIZATION, + bearer_auth().await, + ) .body(axum::body::Body::from_stream(body)) .unwrap(), ) @@ -1412,6 +1516,10 @@ mod tests { .method(axum::http::Method::GET) .uri(uri) .header(axum::http::header::CONTENT_TYPE, "image/jpeg") + .header( + axum::http::header::AUTHORIZATION, + bearer_auth().await, + ) .body(axum::body::Body::empty()) .unwrap(), ) @@ -1680,6 +1788,7 @@ mod tests { .method(axum::http::Method::PATCH) .uri(crate::callers::endpoints::QUEUESONGDATAWIPE) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header(axum::http::header::AUTHORIZATION, bearer_auth().await) .body(axum::body::Body::from(payload.to_string())) .unwrap(), ) @@ -1800,6 +1909,10 @@ mod tests { axum::http::header::CONTENT_TYPE, "application/json", ) + .header( + axum::http::header::AUTHORIZATION, + bearer_auth().await, + ) .body(axum::body::Body::from(payload.to_string())) .unwrap(), ) @@ -1873,6 +1986,10 @@ mod tests { .method(axum::http::Method::GET) .uri(uri) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header( + axum::http::header::AUTHORIZATION, + super::bearer_auth().await, + ) .body(axum::body::Body::empty()) .unwrap(), ) @@ -1926,6 +2043,10 @@ mod tests { .method(axum::http::Method::GET) .uri(uri) .header(axum::http::header::CONTENT_TYPE, "application/json") + .header( + axum::http::header::AUTHORIZATION, + super::bearer_auth().await, + ) .body(axum::body::Body::empty()) .unwrap(), ) @@ -2011,6 +2132,10 @@ mod tests { axum::http::Request::builder() .method(axum::http::Method::GET) .uri(&uri) + .header( + axum::http::header::AUTHORIZATION, + super::bearer_auth().await, + ) .body(axum::body::Body::empty()) .unwrap(), ) @@ -2066,6 +2191,10 @@ mod tests { axum::http::Request::builder() .method(axum::http::Method::GET) .uri(&uri) + .header( + axum::http::header::AUTHORIZATION, + super::bearer_auth().await, + ) .body(axum::body::Body::empty()) .unwrap(), ) @@ -2122,6 +2251,10 @@ mod tests { axum::http::Request::builder() .method(axum::http::Method::GET) .uri(&uri) + .header( + axum::http::header::AUTHORIZATION, + super::bearer_auth().await, + ) .body(axum::body::Body::empty()) .unwrap(), ) @@ -2257,6 +2390,10 @@ mod tests { axum::http::Request::builder() .method(axum::http::Method::DELETE) .uri(&uri) + .header( + axum::http::header::AUTHORIZATION, + super::bearer_auth().await, + ) .body(axum::body::Body::empty()) .unwrap(), )