summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2021-03-18 19:30:00 +0100
committerDavid Lönnhager <david.l@mullvad.net>2021-03-25 13:41:34 +0100
commit2bf39941c92624fa950308787a76d705f8669530 (patch)
treeb43657b67715aa4ef7f708ee97a3437ab799efb4
parent0b961505356f46196873023cabd6901bf5409924 (diff)
downloadmullvadvpn-2bf39941c92624fa950308787a76d705f8669530.tar.xz
mullvadvpn-2bf39941c92624fa950308787a76d705f8669530.zip
Migrate old rotation interval setting
-rw-r--r--mullvad-types/src/settings/migrations/mod.rs12
-rw-r--r--mullvad-types/src/settings/migrations/v1.rs2
-rw-r--r--mullvad-types/src/settings/migrations/v2.rs174
-rw-r--r--mullvad-types/src/settings/mod.rs20
4 files changed, 190 insertions, 18 deletions
diff --git a/mullvad-types/src/settings/migrations/mod.rs b/mullvad-types/src/settings/migrations/mod.rs
index a732848a7b..0d40e1e564 100644
--- a/mullvad-types/src/settings/migrations/mod.rs
+++ b/mullvad-types/src/settings/migrations/mod.rs
@@ -1,15 +1,17 @@
use super::{Error, Result};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
mod v1;
+mod v2;
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
#[repr(u32)]
pub enum SettingsVersion {
V2 = 2,
+ V3 = 3,
}
-pub const CURRENT_SETTINGS_VERSION: SettingsVersion = SettingsVersion::V2;
+pub const CURRENT_SETTINGS_VERSION: SettingsVersion = SettingsVersion::V3;
impl<'de> Deserialize<'de> for SettingsVersion {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
@@ -18,6 +20,7 @@ impl<'de> Deserialize<'de> for SettingsVersion {
{
match <u32>::deserialize(deserializer)? {
v if v == SettingsVersion::V2 as u32 => Ok(SettingsVersion::V2),
+ v if v == SettingsVersion::V3 as u32 => Ok(SettingsVersion::V3),
v => Err(serde::de::Error::custom(format!(
"{} is not a valid SettingsVersion",
v
@@ -49,7 +52,10 @@ pub fn try_migrate_settings(mut settings_file: &[u8]) -> Result<crate::settings:
return Err(Error::NoMatchingVersion);
}
- for migration in &[Box::new(v1::Migration)] {
+ let migrations: Vec<Box<dyn SettingsMigration>> =
+ vec![Box::new(v1::Migration), Box::new(v2::Migration)];
+
+ for migration in &migrations {
if !migration.version_matches(&mut settings) {
continue;
}
@@ -73,7 +79,7 @@ mod test {
#[test]
#[should_panic]
fn test_deserialization_failure_version_too_big() {
- let _version: SettingsVersion = serde_json::from_str("3").expect("Version too big");
+ let _version: SettingsVersion = serde_json::from_str("4").expect("Version too big");
}
#[test]
diff --git a/mullvad-types/src/settings/migrations/v1.rs b/mullvad-types/src/settings/migrations/v1.rs
index 033c25a580..5d3a2d390d 100644
--- a/mullvad-types/src/settings/migrations/v1.rs
+++ b/mullvad-types/src/settings/migrations/v1.rs
@@ -126,7 +126,7 @@ mod test {
"enable_ipv6": false
}
},
- "settings_version": 2
+ "settings_version": 3
}
"#;
diff --git a/mullvad-types/src/settings/migrations/v2.rs b/mullvad-types/src/settings/migrations/v2.rs
new file mode 100644
index 0000000000..7f57ed9075
--- /dev/null
+++ b/mullvad-types/src/settings/migrations/v2.rs
@@ -0,0 +1,174 @@
+use super::{Error, Result, SettingsVersion};
+use crate::wireguard::{MAX_ROTATION_INTERVAL, MIN_ROTATION_INTERVAL};
+use std::time::Duration;
+
+
+pub(super) struct Migration;
+
+impl super::SettingsMigration for Migration {
+ fn version_matches(&self, settings: &mut serde_json::Value) -> bool {
+ settings
+ .get("settings_version")
+ .map(|version| version == SettingsVersion::V2 as u64)
+ .unwrap_or(false)
+ }
+
+ fn migrate(&self, settings: &mut serde_json::Value) -> Result<()> {
+ // `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::NoMatchingVersion)?
+ .remove("show_beta_releases");
+ }
+
+ let automatic_rotation = || -> Option<u64> {
+ 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["settings_version"] = serde_json::json!(SettingsVersion::V3);
+
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::super::try_migrate_settings;
+ use serde_json;
+
+ const V2_SETTINGS: &str = r#"
+{
+ "account_token": "1234",
+ "relay_settings": {
+ "normal": {
+ "location": {
+ "only": {
+ "country": "se"
+ }
+ },
+ "tunnel": {
+ "only": {
+ "openvpn": {
+ "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": 24
+ },
+ "generic": {
+ "enable_ipv6": false
+ }
+ }
+}
+"#;
+
+ pub const NEW_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 migrated_settings =
+ try_migrate_settings(V2_SETTINGS.as_bytes()).expect("Migration failed");
+ let new_settings = serde_json::from_str(NEW_SETTINGS).unwrap();
+
+ assert_eq!(&migrated_settings, &new_settings);
+ }
+}
diff --git a/mullvad-types/src/settings/mod.rs b/mullvad-types/src/settings/mod.rs
index a69d6f4012..9ea32fdb3b 100644
--- a/mullvad-types/src/settings/mod.rs
+++ b/mullvad-types/src/settings/mod.rs
@@ -55,7 +55,6 @@ pub struct Settings {
/// might be located.
pub tunnel_options: TunnelOptions,
/// Whether to notify users of beta updates.
- #[serde(deserialize_with = "deserialize_show_beta_releases")]
pub show_beta_releases: bool,
/// Specifies settings schema version
#[cfg_attr(target_os = "android", jnix(skip))]
@@ -208,21 +207,14 @@ impl Default for TunnelOptions {
}
}
-/// Used to deserialize the `show_beta_releases` field in the settings struct, as it used to be
-/// a nullable field, but it is no longer.
-fn deserialize_show_beta_releases<'de, D: serde::de::Deserializer<'de>>(
- field: D,
-) -> std::result::Result<bool, D::Error> {
- Option::deserialize(field).map(|value| value.unwrap_or(false))
-}
#[cfg(test)]
mod test {
use super::*;
#[test]
- fn test_deserialization_of_2020_4_format() {
- let old_settings = br#"{
+ fn test_deserialization() {
+ let settings = br#"{
"account_token": "0000000000000000",
"relay_settings": {
"normal": {
@@ -258,16 +250,16 @@ mod test {
},
"wireguard": {
"mtu": null,
- "automatic_rotation": null
+ "rotation_interval": null
},
"generic": {
"enable_ipv6": true
}
},
- "settings_version": 2,
- "show_beta_releases": null
+ "settings_version": 3,
+ "show_beta_releases": false
}"#;
- let _ = Settings::load_from_bytes(old_settings).unwrap();
+ let _ = Settings::load_from_bytes(settings).unwrap();
}
}