summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md3
-rw-r--r--Cargo.lock1
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RemoveDeviceEvent.kt1
-rw-r--r--gui/src/main/daemon-rpc.ts2
-rw-r--r--gui/src/renderer/redux/account/actions.ts2
-rw-r--r--gui/src/shared/daemon-rpc-types.ts1
-rw-r--r--mullvad-api/src/device.rs7
-rw-r--r--mullvad-cli/src/cmds/account.rs13
-rw-r--r--mullvad-daemon/src/device/mod.rs19
-rw-r--r--mullvad-daemon/src/device/service.rs23
-rw-r--r--mullvad-daemon/src/lib.rs5
-rw-r--r--mullvad-management-interface/Cargo.toml1
-rw-r--r--mullvad-management-interface/proto/management_interface.proto5
-rw-r--r--mullvad-management-interface/src/types.rs19
-rw-r--r--mullvad-types/src/device.rs6
15 files changed, 79 insertions, 29 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 46410061ad..3c1f3afe37 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,8 +25,11 @@ Line wrap the file at 100 chars. Th
## [Unreleased]
### Added
- Add option to filter relays by ownership in the desktop apps.
+- Include creation timestamp for devices in the CLI.
### Changed
+- List devices on an account sorted by creation date, oldest to newest, instead of alphabetically.
+
#### Android
- Lowered default MTU to 1280 on Android.
diff --git a/Cargo.lock b/Cargo.lock
index 6ae8a7d7e8..5f377c35d8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1770,6 +1770,7 @@ dependencies = [
name = "mullvad-management-interface"
version = "0.1.0"
dependencies = [
+ "chrono",
"err-derive",
"futures",
"lazy_static",
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RemoveDeviceEvent.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RemoveDeviceEvent.kt
index 3080d7f10a..94499c032a 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RemoveDeviceEvent.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RemoveDeviceEvent.kt
@@ -6,6 +6,5 @@ import kotlinx.parcelize.Parcelize
@Parcelize
data class RemoveDeviceEvent(
val accountToken: String,
- val removedDevice: Device,
val newDevices: ArrayList<Device>
) : Parcelable
diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts
index 166a48c9cb..e704979a40 100644
--- a/gui/src/main/daemon-rpc.ts
+++ b/gui/src/main/daemon-rpc.ts
@@ -1476,11 +1476,13 @@ function convertFromDeviceRemoval(deviceRemoval: grpcTypes.RemoveDeviceEvent): A
}
function convertFromDevice(device: grpcTypes.Device): IDevice {
+ const created = ensureExists(device.getCreated(), "no 'created' field for device").toDate();
const asObject = device.toObject();
return {
...asObject,
ports: asObject.portsList.map((port) => port.id),
+ created: created,
};
}
diff --git a/gui/src/renderer/redux/account/actions.ts b/gui/src/renderer/redux/account/actions.ts
index cfa672a407..594a96f71f 100644
--- a/gui/src/renderer/redux/account/actions.ts
+++ b/gui/src/renderer/redux/account/actions.ts
@@ -213,7 +213,7 @@ function updateAccountExpiry(expiry?: string): IUpdateAccountExpiryAction {
function updateDevices(devices: Array<IDevice>): IUpdateDevicesAction {
return {
type: 'UPDATE_DEVICES',
- devices: devices.sort((a, b) => a.name.localeCompare(b.name)),
+ devices: devices.sort((a, b) => a.created.getDate() - b.created.getDate()),
};
}
diff --git a/gui/src/shared/daemon-rpc-types.ts b/gui/src/shared/daemon-rpc-types.ts
index 6e70c3f9b6..45af1c605e 100644
--- a/gui/src/shared/daemon-rpc-types.ts
+++ b/gui/src/shared/daemon-rpc-types.ts
@@ -359,6 +359,7 @@ export interface IDevice {
id: string;
name: string;
ports?: Array<string>;
+ created: Date;
}
export interface IDeviceRemoval {
diff --git a/mullvad-api/src/device.rs b/mullvad-api/src/device.rs
index c3cd253826..af7cbd0a8e 100644
--- a/mullvad-api/src/device.rs
+++ b/mullvad-api/src/device.rs
@@ -1,3 +1,4 @@
+use chrono::{DateTime, Utc};
use http::{Method, StatusCode};
use mullvad_types::{
account::AccountToken,
@@ -23,6 +24,8 @@ struct DeviceResponse {
ipv4_address: ipnetwork::Ipv4Network,
ipv6_address: ipnetwork::Ipv6Network,
ports: Vec<DevicePort>,
+ hijack_dns: bool,
+ created: DateTime<Utc>,
}
impl DevicesProxy {
@@ -71,6 +74,8 @@ impl DevicesProxy {
ipv4_address,
ipv6_address,
ports,
+ hijack_dns,
+ created,
..
} = response;
@@ -80,6 +85,8 @@ impl DevicesProxy {
name,
pubkey,
ports,
+ hijack_dns,
+ created,
},
mullvad_types::wireguard::AssociatedAddresses {
ipv4_address,
diff --git a/mullvad-cli/src/cmds/account.rs b/mullvad-cli/src/cmds/account.rs
index c6c723c5ce..8432f1da0a 100644
--- a/mullvad-cli/src/cmds/account.rs
+++ b/mullvad-cli/src/cmds/account.rs
@@ -156,6 +156,10 @@ impl Account {
if verbose {
println!("Device id : {}", inner_device.id);
println!("Device pubkey : {}", inner_device.pubkey);
+ println!(
+ "Device created : {}",
+ inner_device.created.with_timezone(&chrono::Local)
+ );
for port in inner_device.ports {
println!("Device port : {}", port);
}
@@ -184,7 +188,7 @@ impl Account {
async fn list_devices(&self, matches: &clap::ArgMatches) -> Result<()> {
let mut rpc = new_rpc_client().await?;
let token = self.parse_account_else_current(&mut rpc, matches).await?;
- let device_list = rpc
+ let mut device_list = rpc
.list_devices(token)
.await
.map_err(map_device_error)?
@@ -193,6 +197,9 @@ impl Account {
let verbose = matches.is_present("verbose");
println!("Devices on the account:");
+ device_list
+ .devices
+ .sort_unstable_by_key(|dev| dev.created.as_ref().map(|dt| dt.seconds).unwrap_or(0));
for device in device_list.devices {
let device = Device::try_from(device.clone()).unwrap();
if verbose {
@@ -200,6 +207,10 @@ impl Account {
println!("Name : {}", device.pretty_name());
println!("Id : {}", device.id);
println!("Public key: {}", device.pubkey);
+ println!(
+ "Created : {}",
+ device.created.with_timezone(&chrono::Local)
+ );
for port in device.ports {
println!("Port : {}", port);
}
diff --git a/mullvad-daemon/src/device/mod.rs b/mullvad-daemon/src/device/mod.rs
index 84fa1afa50..4d420a404a 100644
--- a/mullvad-daemon/src/device/mod.rs
+++ b/mullvad-daemon/src/device/mod.rs
@@ -157,6 +157,19 @@ pub struct PrivateDevice {
pub name: DeviceName,
pub wg_data: wireguard::WireguardData,
pub ports: Vec<DevicePort>,
+ // FIXME: Reasonable default to avoid migration code for the field,
+ // as it was previously missing.
+ // This attribute may be removed once upgrades from `2022.2-beta1`
+ // no longer need to be supported.
+ #[serde(default)]
+ pub hijack_dns: bool,
+ // FIXME: Incorrect but reasonable default to avoid migration code
+ // for the field, as it was previously missing.
+ // The value is corrected when the device is validated or updated.
+ // This attribute may be removed once upgrades from `2022.2-beta1`
+ // no longer need to be supported.
+ #[serde(default = "Utc::now")]
+ pub created: DateTime<Utc>,
}
impl PrivateDevice {
@@ -174,6 +187,8 @@ impl PrivateDevice {
name: device.name,
wg_data,
ports: device.ports,
+ hijack_dns: device.hijack_dns,
+ created: device.created,
})
}
@@ -186,6 +201,8 @@ impl PrivateDevice {
self.id = device.id;
self.ports = device.ports;
self.name = device.name;
+ self.hijack_dns = device.hijack_dns;
+ self.created = device.created;
Ok(())
}
}
@@ -197,6 +214,8 @@ impl From<PrivateDevice> for Device {
ports: device.ports,
pubkey: device.wg_data.private_key.public_key(),
name: device.name,
+ hijack_dns: device.hijack_dns,
+ created: device.created,
}
}
}
diff --git a/mullvad-daemon/src/device/service.rs b/mullvad-daemon/src/device/service.rs
index f4f147ac96..90a44d5fe1 100644
--- a/mullvad-daemon/src/device/service.rs
+++ b/mullvad-daemon/src/device/service.rs
@@ -109,27 +109,10 @@ impl DeviceService {
&self,
account_token: AccountToken,
device_id: DeviceId,
- ) -> Result<(Device, Vec<Device>), Error> {
- let mut devices = self.list_devices(account_token.clone()).await?;
- self.remove_device_inner(account_token, device_id.clone())
+ ) -> Result<Vec<Device>, Error> {
+ self.remove_device_inner(account_token.clone(), device_id)
.await?;
- if let Some(index) = devices.iter().position(|device| device.id == device_id) {
- Ok((devices.swap_remove(index), devices))
- } else {
- // You would only end up here if the API service successfully removed a device that
- // was previously not included in the list returned by it, which should be impossible.
- // Just return a bogus device in its place.
- log::error!("List did not contain the revoked device");
- Ok((
- Device {
- id: device_id,
- name: "unknown device".to_string(),
- pubkey: talpid_types::net::wireguard::PublicKey::from([0u8; 32]),
- ports: vec![],
- },
- devices,
- ))
- }
+ self.list_devices(account_token).await
}
async fn remove_device_inner(
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index fbc27bcc92..b75ad9121c 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -1393,10 +1393,11 @@ where
let result = device_service
.remove_device(account_token.clone(), device_id)
.await
- .map(move |(removed_device, new_devices)| {
+ .map(move |new_devices| {
+ // FIXME: We should be able to get away with only returning the removed ID,
+ // and not have to request the list from the API.
event_listener.notify_remove_device_event(RemoveDeviceEvent {
account_token,
- removed_device,
new_devices,
});
});
diff --git a/mullvad-management-interface/Cargo.toml b/mullvad-management-interface/Cargo.toml
index e6b00a36b1..ca1853855b 100644
--- a/mullvad-management-interface/Cargo.toml
+++ b/mullvad-management-interface/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2021"
publish = false
[dependencies]
+chrono = { version = "0.4.19" }
err-derive = "0.3.1"
mullvad-types = { path = "../mullvad-types" }
mullvad-paths = { path = "../mullvad-paths" }
diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto
index 42a73ca7bd..20d5318a58 100644
--- a/mullvad-management-interface/proto/management_interface.proto
+++ b/mullvad-management-interface/proto/management_interface.proto
@@ -580,6 +580,8 @@ message Device {
string name = 2;
bytes pubkey = 3;
repeated DevicePort ports = 4;
+ bool hijack_dns = 5;
+ google.protobuf.Timestamp created = 6;
}
message DevicePort {
@@ -619,6 +621,5 @@ message DeviceEvent {
message RemoveDeviceEvent {
string account_token = 1;
- Device removed_device = 2;
- repeated Device new_device_list = 3;
+ repeated Device new_device_list = 2;
}
diff --git a/mullvad-management-interface/src/types.rs b/mullvad-management-interface/src/types.rs
index 4785f7221d..54aac8964f 100644
--- a/mullvad-management-interface/src/types.rs
+++ b/mullvad-management-interface/src/types.rs
@@ -217,6 +217,11 @@ impl From<mullvad_types::device::Device> for Device {
name: device.name,
pubkey: device.pubkey.as_bytes().to_vec(),
ports: device.ports.into_iter().map(DevicePort::from).collect(),
+ hijack_dns: device.hijack_dns,
+ created: Some(Timestamp {
+ seconds: device.created.timestamp(),
+ nanos: 0,
+ }),
}
}
}
@@ -276,7 +281,6 @@ impl From<mullvad_types::device::RemoveDeviceEvent> for RemoveDeviceEvent {
fn from(event: mullvad_types::device::RemoveDeviceEvent) -> Self {
RemoveDeviceEvent {
account_token: event.account_token,
- removed_device: Some(Device::from(event.removed_device)),
new_device_list: event.new_devices.into_iter().map(Device::from).collect(),
}
}
@@ -808,6 +812,19 @@ impl TryFrom<Device> for mullvad_types::device::Device {
.into_iter()
.map(mullvad_types::device::DevicePort::from)
.collect(),
+ hijack_dns: device.hijack_dns,
+ created: chrono::DateTime::from_utc(
+ chrono::NaiveDateTime::from_timestamp(
+ device
+ .created
+ .ok_or(FromProtobufTypeError::InvalidArgument(
+ "missing 'created' field",
+ ))?
+ .seconds,
+ 0,
+ ),
+ chrono::Utc,
+ ),
})
}
}
diff --git a/mullvad-types/src/device.rs b/mullvad-types/src/device.rs
index df7ba7bef0..38dd528fd5 100644
--- a/mullvad-types/src/device.rs
+++ b/mullvad-types/src/device.rs
@@ -1,4 +1,5 @@
use crate::account::AccountToken;
+use chrono::{DateTime, Utc};
#[cfg(target_os = "android")]
use jnix::IntoJava;
use serde::{Deserialize, Serialize};
@@ -21,6 +22,10 @@ pub struct Device {
#[cfg_attr(target_os = "android", jnix(map = "|key| *key.as_bytes()"))]
pub pubkey: PublicKey,
pub ports: Vec<DevicePort>,
+ #[cfg_attr(target_os = "android", jnix(skip))]
+ pub hijack_dns: bool,
+ #[cfg_attr(target_os = "android", jnix(skip))]
+ pub created: DateTime<Utc>,
}
impl Eq for Device {}
@@ -132,6 +137,5 @@ pub struct DeviceEvent {
#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))]
pub struct RemoveDeviceEvent {
pub account_token: AccountToken,
- pub removed_device: Device,
pub new_devices: Vec<Device>,
}