#![allow(clippy::identity_op)] use super::{Error, Result}; use mullvad_types::settings::SettingsVersion; use std::time::Duration; // ====================================================== // Section for vendoring types and values that // this settings version depend on. See `mod.rs`. pub const MIN_ROTATION_INTERVAL: Duration = Duration::from_secs(1 * 24 * 60 * 60); pub const MAX_ROTATION_INTERVAL: Duration = Duration::from_secs(7 * 24 * 60 * 60); // ====================================================== pub fn migrate(settings: &mut serde_json::Value) -> Result<()> { if !version_matches(settings) { return Ok(()); } log::info!("Migrating settings format to V3"); // `show_beta_releases` used to be nullable if settings .get_mut("show_beta_releases") .map(|val| val.is_null()) .unwrap_or(false) { settings .as_object_mut() .ok_or(Error::InvalidSettingsContent)? .remove("show_beta_releases"); } let automatic_rotation = || -> Option { settings .get("tunnel_options")? .get("wireguard")? .get("automatic_rotation") .map(|ivl| ivl.as_u64())? }(); if let Some(interval) = automatic_rotation { let new_ivl = match Duration::from_secs(60 * 60 * interval) { ivl if ivl < MIN_ROTATION_INTERVAL => { log::warn!("Increasing key rotation interval since it is below minimum"); MIN_ROTATION_INTERVAL } ivl if ivl > MAX_ROTATION_INTERVAL => { log::warn!("Decreasing key rotation interval since it is above maximum"); MAX_ROTATION_INTERVAL } ivl => ivl, }; settings["tunnel_options"]["wireguard"]["rotation_interval"] = serde_json::json!(new_ivl); settings["tunnel_options"]["wireguard"] .as_object_mut() .ok_or(Error::InvalidSettingsContent)? .remove("automatic_rotation"); } settings["settings_version"] = serde_json::json!(SettingsVersion::V3); Ok(()) } fn version_matches(settings: &serde_json::Value) -> bool { settings .get("settings_version") .map(|version| version == SettingsVersion::V2 as u64) .unwrap_or(false) } #[cfg(test)] mod test { use super::{migrate, version_matches}; const V2_SETTINGS: &str = r#" { "account_token": "1234", "relay_settings": { "normal": { "location": { "only": { "country": "se" } }, "tunnel_protocol": "any", "wireguard_constraints": { "port": "any" }, "openvpn_constraints": { "port": { "only": 53 }, "protocol": { "only": "udp" } } } }, "bridge_settings": { "normal": { "location": "any" } }, "bridge_state": "auto", "allow_lan": true, "block_when_disconnected": false, "auto_connect": false, "tunnel_options": { "openvpn": { "mssfix": null }, "wireguard": { "mtu": null, "automatic_rotation": 10 }, "generic": { "enable_ipv6": false } }, "show_beta_releases": null, "settings_version": 2 } "#; pub const V3_SETTINGS: &str = r#" { "account_token": "1234", "relay_settings": { "normal": { "location": { "only": { "country": "se" } }, "tunnel_protocol": "any", "wireguard_constraints": { "port": "any" }, "openvpn_constraints": { "port": { "only": 53 }, "protocol": { "only": "udp" } } } }, "bridge_settings": { "normal": { "location": "any" } }, "bridge_state": "auto", "allow_lan": true, "block_when_disconnected": false, "auto_connect": false, "tunnel_options": { "openvpn": { "mssfix": null }, "wireguard": { "mtu": null, "rotation_interval": { "secs": 86400, "nanos": 0 } }, "generic": { "enable_ipv6": false } }, "settings_version": 3 } "#; #[test] fn test_v2_migration() { let mut old_settings = serde_json::from_str(V2_SETTINGS).unwrap(); assert!(version_matches(&old_settings)); migrate(&mut old_settings).unwrap(); let new_settings: serde_json::Value = serde_json::from_str(V3_SETTINGS).unwrap(); assert_eq!(&old_settings, &new_settings); } }