From 42b596479025de8882c12948ee0e0deb13358969 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Fri, 25 Apr 2025 23:09:57 +0200 Subject: [PATCH] More API Response fixes - Some 2fa checks - Some org checks - Reconfigured the experimental flags and noted which are deprecated Also removed some hard-coded defaults. - Updated crates Signed-off-by: BlackDex --- Cargo.lock | 41 ++++++++++++++++++------ src/api/core/mod.rs | 8 +++-- src/api/core/two_factor/authenticator.rs | 4 +++ src/api/core/two_factor/duo.rs | 3 ++ src/config.rs | 16 +++++++-- src/db/models/device.rs | 2 ++ src/db/models/org_policy.rs | 14 ++++++-- src/db/models/organization.rs | 2 -- src/util.rs | 13 ++++++-- 9 files changed, 80 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 051f2656..82660a5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -490,9 +490,9 @@ checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" [[package]] name = "cc" -version = "1.2.19" +version = "1.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" dependencies = [ "shlex", ] @@ -1340,7 +1340,7 @@ dependencies = [ "futures-sink", "futures-timer", "futures-util", - "getrandom 0.3.3", + "getrandom 0.3.2", "hashbrown 0.15.3", "nonzero_ext", "parking_lot", @@ -2699,7 +2699,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy", + "zerocopy 0.8.25", ] [[package]] @@ -3011,9 +3011,9 @@ dependencies = [ [[package]] name = "resolv-conf" -version = "0.7.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" +checksum = "fc7c8f7f733062b66dc1c63f9db168ac0b97a9210e247fa90fdc9ad08f51b302" [[package]] name = "ring" @@ -3934,7 +3934,8 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.7.7", + "toml_write", + "winnow 0.7.8", ] [[package]] @@ -4811,9 +4812,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" +checksum = "9e27d6ad3dac991091e4d35de9ba2d2d00647c5d0fc26c5496dee55984ae111b" dependencies = [ "memchr", ] @@ -4904,7 +4905,27 @@ version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive 0.8.25", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index 5b38c24d..f5a3f420 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -199,12 +199,14 @@ fn get_api_webauthn(_headers: Headers) -> Json { #[get("/config")] fn config() -> Json { let domain = crate::CONFIG.domain(); + // Official available feature flags can be found here: + // Server (v2025.4.2): https://github.com/bitwarden/server/blob/9ad96375153113abff36d28a3465f1c51ea604a0/src/Core/Constants.cs#L102 + // Client (v2025.4.0): https://github.com/bitwarden/clients/blob/c86c73563140412ca8359cad0fedc6f74b29db84/libs/common/src/enums/feature-flag.enum.ts#L10 + // Android (v2025.2.0): https://github.com/bitwarden/android/blob/8cd289cc89f729062f094d47b92c98b09c605e71/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FlagKey.kt#L27 let mut feature_states = parse_experimental_client_feature_flags(&crate::CONFIG.experimental_client_feature_flags()); // Force the new key rotation feature - feature_states.insert("key-rotation-improvements".to_string(), true); feature_states.insert("duo-redirect".to_string(), true); - feature_states.insert("flexible-collections-v-1".to_string(), false); feature_states.insert("email-verification".to_string(), true); feature_states.insert("unauth-ui-refresh".to_string(), true); @@ -217,7 +219,6 @@ fn config() -> Json { // - Individual cipher key encryption: 2024.2.0 "version": "2025.1.0", "gitHash": option_env!("GIT_REV"), - "cloudRegion": null, "server": { "name": "Vaultwarden", "url": "https://github.com/dani-garcia/vaultwarden" @@ -231,6 +232,7 @@ fn config() -> Json { "identity": format!("{domain}/identity"), "notifications": format!("{domain}/notifications"), "sso": "", + "cloudRegion": null, }, // Bitwarden uses this for the self-hosted servers to indicate the default push technology "push": { diff --git a/src/api/core/two_factor/authenticator.rs b/src/api/core/two_factor/authenticator.rs index 3bdb1226..0769db80 100644 --- a/src/api/core/two_factor/authenticator.rs +++ b/src/api/core/two_factor/authenticator.rs @@ -34,6 +34,10 @@ async fn generate_authenticator(data: Json, headers: Headers, _ => (false, crypto::encode_random_bytes::<20>(BASE32)), }; + // Upstream seems to also return `userVerificationToken`, but doesn't seem to be used at all. + // It should help prevent TOTP disclosure if someone keeps there vault unlocked. + // Since it doesn't seem to be used, and also does not causes any issues, lets leave it out of the response. + // See: https://github.com/bitwarden/server/blob/b00f11fc43c0d5f20a8e224a8f9e3fc4dc23e60e/src/Api/Auth/Controllers/TwoFactorController.cs#L94 Ok(Json(json!({ "enabled": enabled, "key": key, diff --git a/src/api/core/two_factor/duo.rs b/src/api/core/two_factor/duo.rs index 7f7d42ba..6600bfde 100644 --- a/src/api/core/two_factor/duo.rs +++ b/src/api/core/two_factor/duo.rs @@ -118,6 +118,9 @@ async fn get_duo(data: Json, headers: Headers, mut conn: DbCo } else { json!({ "enabled": enabled, + "host": null, + "clientSecret": null, + "clientId": null, "object": "twoFactorDuo" }) }; diff --git a/src/config.rs b/src/config.rs index 3d001417..60a6e78e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -579,7 +579,7 @@ make_config! { authenticator_disable_time_drift: bool, true, def, false; /// Customize the enabled feature flags on the clients |> This is a comma separated list of feature flags to enable. - experimental_client_feature_flags: String, false, def, "fido2-vault-credentials".to_string(); + experimental_client_feature_flags: String, false, def, String::new(); /// Require new device emails |> When a user logs in an email is required to be sent. /// If sending the email fails the login attempt will fail. @@ -834,20 +834,30 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { } // TODO: deal with deprecated flags so they can be removed from this list, cf. #4263 + // Server (v2025.4.2): https://github.com/bitwarden/server/blob/9ad96375153113abff36d28a3465f1c51ea604a0/src/Core/Constants.cs#L102 + // Client (v2025.4.0): https://github.com/bitwarden/clients/blob/c86c73563140412ca8359cad0fedc6f74b29db84/libs/common/src/enums/feature-flag.enum.ts#L10 + // Android (v2025.2.0): https://github.com/bitwarden/android/blob/8cd289cc89f729062f094d47b92c98b09c605e71/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FlagKey.kt#L27 const KNOWN_FLAGS: &[&str] = &[ + // Start Deprecated "autofill-overlay", "autofill-v2", "browser-fileless-import", "extension-refresh", "fido2-vault-credentials", + // End Deprecated + // Autofill Team "inline-menu-positioning-improvements", - "ssh-key-vault-item", + "inline-menu-totp", "ssh-agent", + // Key Management Team + "ssh-key-vault-item", + // Tools + "export-attachments", + // Mobile Team "anon-addy-self-host-alias", "simple-login-self-host-alias", "mutual-tls", "export-attachments", - "inline-menu-totp", ]; let configured_flags = parse_experimental_client_feature_flags(&cfg.experimental_client_feature_flags); let invalid_flags: Vec<_> = configured_flags.keys().filter(|flag| !KNOWN_FLAGS.contains(&flag.as_str())).collect(); diff --git a/src/db/models/device.rs b/src/db/models/device.rs index 4a28480e..6c0881d1 100644 --- a/src/db/models/device.rs +++ b/src/db/models/device.rs @@ -167,6 +167,8 @@ impl DeviceWithAuthRequest { "creationDate": format_date(&self.device.created_at), "devicePendingAuthRequest": auth_request, "isTrusted": false, + "encryptedPublicKey": null, + "encryptedUserKey": null, "object": "device", }) } diff --git a/src/db/models/org_policy.rs b/src/db/models/org_policy.rs index 7f139266..e1a577ba 100644 --- a/src/db/models/org_policy.rs +++ b/src/db/models/org_policy.rs @@ -83,14 +83,24 @@ impl OrgPolicy { pub fn to_json(&self) -> Value { let data_json: Value = serde_json::from_str(&self.data).unwrap_or(Value::Null); - json!({ + let mut policy = json!({ "id": self.uuid, "organizationId": self.org_uuid, "type": self.atype, "data": data_json, "enabled": self.enabled, "object": "policy", - }) + }); + + // Upstream adds this key/value + // Allow enabling Single Org policy when the organization has claimed domains. + // See: (https://github.com/bitwarden/server/pull/5565) + // We return the same to prevent possible issues + if self.atype == 8i32 { + policy["canToggleState"] = json!(true); + } + + policy } } diff --git a/src/db/models/organization.rs b/src/db/models/organization.rs index 547726ca..16b27bbd 100644 --- a/src/db/models/organization.rs +++ b/src/db/models/organization.rs @@ -203,7 +203,6 @@ impl Organization { "useResetPassword": CONFIG.mail_enabled(), "allowAdminAccessToAllCollectionItems": true, "limitCollectionCreation": true, - "limitCollectionCreationDeletion": true, "limitCollectionDeletion": true, "businessName": self.name, @@ -468,7 +467,6 @@ impl Membership { "familySponsorshipToDelete": null, "accessSecretsManager": false, "limitCollectionCreation": self.atype < MembershipType::Manager, // If less then a manager return true, to limit collection creations - "limitCollectionCreationDeletion": true, "limitCollectionDeletion": true, "limitItemDeletion": false, "allowAdminAccessToAllCollectionItems": true, diff --git a/src/util.rs b/src/util.rs index c72f23ce..e71a03b0 100644 --- a/src/util.rs +++ b/src/util.rs @@ -752,9 +752,16 @@ pub fn convert_json_key_lcase_first(src_json: Value) -> Value { /// Parses the experimental client feature flags string into a HashMap. pub fn parse_experimental_client_feature_flags(experimental_client_feature_flags: &str) -> HashMap { - let feature_states = experimental_client_feature_flags.split(',').map(|f| (f.trim().to_owned(), true)).collect(); - - feature_states + experimental_client_feature_flags + .split(',') + .filter_map(|f| { + let flag = f.trim(); + if !flag.is_empty() { + return Some((flag.to_owned(), true)); + } + None + }) + .collect() } /// TODO: This is extracted from IpAddr::is_global, which is unstable: