diff --git a/src/config.rs b/src/config.rs index 627c3bc..5a1d2cc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -38,7 +38,7 @@ pub struct ConfigUser { pub admin: bool, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct Config { pub album_art_pattern: Option, pub reindex_every_n_seconds: Option, diff --git a/src/service/error.rs b/src/service/error.rs index 0ff3340..214d3bc 100644 --- a/src/service/error.rs +++ b/src/service/error.rs @@ -4,6 +4,8 @@ use thiserror::Error; pub enum APIError { #[error("Incorrect Credentials")] IncorrectCredentials, + #[error("Cannot remove own admin privilege")] + OwnAdminPrivilegeRemoval, #[error("Unspecified")] Unspecified, } diff --git a/src/service/rocket/api.rs b/src/service/rocket/api.rs index 64f616c..cf8cb80 100644 --- a/src/service/rocket/api.rs +++ b/src/service/rocket/api.rs @@ -61,7 +61,8 @@ impl<'r> rocket::response::Responder<'r> for APIError { fn respond_to(self, _: &rocket::request::Request<'_>) -> rocket::response::Result<'r> { let status = match self { APIError::IncorrectCredentials => rocket::http::Status::Unauthorized, - _ => rocket::http::Status::InternalServerError, + APIError::OwnAdminPrivilegeRemoval => rocket::http::Status::Conflict, + APIError::Unspecified => rocket::http::Status::InternalServerError, }; rocket::response::Response::build().status(status).ok() } @@ -216,19 +217,23 @@ fn get_settings(db: State<'_, DB>, _admin_rights: AdminRights) -> Result, admin_rights: AdminRights, config: Json) -> Result<()> { +fn put_settings( + db: State<'_, DB>, + admin_rights: AdminRights, + config: Json, +) -> Result<(), APIError> { // Do not let users remove their own admin rights - let mut sanitized_config = config.clone(); - if let Some(users) = &mut sanitized_config.users { - for user in users.iter_mut() { - if let Some(auth) = &admin_rights.auth { - if auth.username == user.name { - user.admin = true; + if let Some(auth) = &admin_rights.auth { + if let Some(users) = &config.users { + for user in users { + if auth.username == user.name && !user.admin { + return Err(APIError::OwnAdminPrivilegeRemoval); } } } } - config::amend(&db, &sanitized_config)?; + + config::amend(&db, &config)?; Ok(()) } diff --git a/src/service/test.rs b/src/service/test.rs index b178c35..ac8bc6b 100644 --- a/src/service/test.rs +++ b/src/service/test.rs @@ -9,7 +9,7 @@ use std::time::Duration; use crate::service::constants::*; use crate::service::dto; -use crate::{config, ddns, index, vfs}; +use crate::{config, index, vfs}; #[cfg(feature = "service-rocket")] pub use crate::service::rocket::test::ServiceType; @@ -143,84 +143,28 @@ fn test_service_settings() { assert!(service.get("/api/settings").status() == StatusCode::UNAUTHORIZED); service.login(); - { - let response = service.get_json::("/api/settings"); - let configuration = response.body(); - assert_eq!( - configuration, - &config::Config { - album_art_pattern: Some("Folder.(jpg|png)".to_string()), - reindex_every_n_seconds: Some(1800), - mount_dirs: Some(vec![vfs::MountPoint { - name: TEST_MOUNT_NAME.into(), - source: TEST_MOUNT_SOURCE.into() - }]), - prefix_url: None, - users: Some(vec![config::ConfigUser { - name: TEST_USERNAME.into(), - password: "".into(), - admin: true - }]), - ydns: Some(ddns::DDNSConfig { - host: "".into(), - username: "".into(), - password: "".into() - }), - } - ); - } - - let mut configuration = config::Config { - album_art_pattern: Some("my_pattern".to_owned()), - reindex_every_n_seconds: Some(3600), - mount_dirs: Some(vec![ - vfs::MountPoint { - name: TEST_MOUNT_NAME.into(), - source: TEST_MOUNT_SOURCE.into(), - }, - vfs::MountPoint { - name: "more_music".into(), - source: "test-data/small-collection".into(), - }, - ]), - prefix_url: Some("my_prefix".to_owned()), - users: Some(vec![ - config::ConfigUser { - name: "test_user".into(), - password: "some_password".into(), - admin: false, - }, - config::ConfigUser { - name: "other_user".into(), - password: "some_other_password".into(), - admin: false, - }, - ]), - ydns: Some(ddns::DDNSConfig { - host: "my_host".into(), - username: "my_username".into(), - password: "my_password".into(), - }), - }; - - service.put_json("/api/settings", &configuration); - - configuration.users = Some(vec![ - config::ConfigUser { - name: "test_user".into(), - password: "".into(), - admin: true, - }, - config::ConfigUser { - name: "other_user".into(), - password: "".into(), - admin: false, - }, - ]); - let response = service.get_json::("/api/settings"); - let received = response.body(); - assert_eq!(received, &configuration); + assert_eq!(response.status(), StatusCode::OK); + + let configuration = config::Config::default(); + let response = service.put_json("/api/settings", &configuration); + assert_eq!(response.status(), StatusCode::OK); +} + +#[test] +fn test_service_settings_cannot_unadmin_self() { + let mut service = ServiceType::new(&format!("{}{}", TEST_DB_PREFIX, line!())); + service.complete_initial_setup(); + service.login(); + + let mut configuration = config::Config::default(); + configuration.users = Some(vec![config::ConfigUser { + name: TEST_USERNAME.into(), + password: "".into(), + admin: false, + }]); + let response = service.put_json("/api/settings", &configuration); + assert_eq!(response.status(), StatusCode::CONFLICT); } #[test]