summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2022-05-02 16:17:19 +0200
committerDavid Lönnhager <david.l@mullvad.net>2022-05-05 10:08:29 +0200
commit79597f626eefee882e468919b1c0e2123b612ef2 (patch)
tree930f8e9b6b96d90081555ce9dbccdcccb2f437b9
parent24742485bd400e84ba7279b992867f0d9de9f4b1 (diff)
downloadmullvadvpn-79597f626eefee882e468919b1c0e2123b612ef2.tar.xz
mullvadvpn-79597f626eefee882e468919b1c0e2123b612ef2.zip
Hide private device type in `mullvad-daemon`
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountAndDevice.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceConfig.kt)4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceEvent.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt6
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt6
-rw-r--r--mullvad-daemon/src/device/api.rs18
-rw-r--r--mullvad-daemon/src/device/mod.rs231
-rw-r--r--mullvad-daemon/src/device/service.rs52
-rw-r--r--mullvad-daemon/src/lib.rs59
-rw-r--r--mullvad-daemon/src/migrations/device.rs83
-rw-r--r--mullvad-jni/src/classes.rs2
-rw-r--r--mullvad-jni/src/daemon_interface.rs4
-rw-r--r--mullvad-management-interface/src/types.rs8
-rw-r--r--mullvad-setup/src/main.rs2
-rw-r--r--mullvad-types/src/device.rs68
14 files changed, 298 insertions, 247 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceConfig.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountAndDevice.kt
index b8f5664e30..1a4ed323b9 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceConfig.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountAndDevice.kt
@@ -4,7 +4,7 @@ import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
-data class DeviceConfig(
- val token: String,
+data class AccountAndDevice(
+ val account_token: String,
val device: Device
) : Parcelable
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceEvent.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceEvent.kt
index 24703e7066..1f2b68fbe5 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceEvent.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceEvent.kt
@@ -5,6 +5,6 @@ import kotlinx.parcelize.Parcelize
@Parcelize
data class DeviceEvent(
- val device: DeviceConfig?,
+ val device: AccountAndDevice?,
val remote: Boolean
) : Parcelable
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt
index a4fd52cabb..8903cae6a9 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt
@@ -8,7 +8,7 @@ sealed class DeviceState : Parcelable {
object InitialState : DeviceState()
@Parcelize
- data class DeviceRegistered(val deviceConfig: DeviceConfig) : DeviceState()
+ data class DeviceRegistered(val deviceConfig: AccountAndDevice) : DeviceState()
@Parcelize
object DeviceNotRegistered : DeviceState()
@@ -22,11 +22,11 @@ sealed class DeviceState : Parcelable {
}
fun token(): String? {
- return (this as? DeviceRegistered)?.deviceConfig?.token
+ return (this as? DeviceRegistered)?.deviceConfig?.account_token
}
companion object {
- fun fromDeviceConfig(deviceConfig: DeviceConfig?): DeviceState {
+ fun fromDeviceConfig(deviceConfig: AccountAndDevice?): DeviceState {
return deviceConfig?.let { DeviceRegistered(it) } ?: DeviceNotRegistered
}
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt
index 2aefe71a91..c7c2bb1e95 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt
@@ -3,9 +3,9 @@ package net.mullvad.mullvadvpn.service
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
+import net.mullvad.mullvadvpn.model.AccountAndDevice
import net.mullvad.mullvadvpn.model.AppVersionInfo
import net.mullvad.mullvadvpn.model.Device
-import net.mullvad.mullvadvpn.model.DeviceConfig
import net.mullvad.mullvadvpn.model.DeviceEvent
import net.mullvad.mullvadvpn.model.DeviceState
import net.mullvad.mullvadvpn.model.DnsOptions
@@ -126,7 +126,7 @@ class MullvadDaemon(vpnService: MullvadVpnService) {
return listDevices(daemonInterfaceAddress, accountToken)
}
- fun getDevice(): DeviceConfig? = getDevice(daemonInterfaceAddress)
+ fun getDevice(): AccountAndDevice? = getDevice(daemonInterfaceAddress)
fun updateDevice() = updateDevice(daemonInterfaceAddress)
@@ -216,7 +216,7 @@ class MullvadDaemon(vpnService: MullvadVpnService) {
accountToken: String?
): List<Device>?
- private external fun getDevice(daemonInterfaceAddress: Long): DeviceConfig?
+ private external fun getDevice(daemonInterfaceAddress: Long): AccountAndDevice?
private external fun updateDevice(daemonInterfaceAddress: Long)
private external fun removeDevice(
daemonInterfaceAddress: Long,
diff --git a/mullvad-daemon/src/device/api.rs b/mullvad-daemon/src/device/api.rs
index 9005c41364..cee0186987 100644
--- a/mullvad-daemon/src/device/api.rs
+++ b/mullvad-daemon/src/device/api.rs
@@ -1,13 +1,11 @@
use std::pin::Pin;
use futures::{future::FusedFuture, Future};
-use mullvad_types::{
- device::{Device, DeviceData},
- wireguard::WireguardData,
-};
+use mullvad_types::{device::Device, wireguard::WireguardData};
-use super::{Error, ResponseTx};
-pub struct CurrentApiCall {
+use super::{Error, PrivateAccountAndDevice, ResponseTx};
+
+pub(crate) struct CurrentApiCall {
current_call: Option<Call>,
}
@@ -20,7 +18,7 @@ impl CurrentApiCall {
self.current_call = None;
}
- pub fn set_login(&mut self, login: ApiCall<DeviceData>, tx: ResponseTx<()>) {
+ pub fn set_login(&mut self, login: ApiCall<PrivateAccountAndDevice>, tx: ResponseTx<()>) {
self.current_call = Some(Call::Login(login, Some(tx)));
}
@@ -89,7 +87,7 @@ impl FusedFuture for CurrentApiCall {
type ApiCall<T> = Pin<Box<dyn Future<Output = Result<T, Error>> + Send>>;
enum Call {
- Login(ApiCall<DeviceData>, Option<ResponseTx<()>>),
+ Login(ApiCall<PrivateAccountAndDevice>, Option<ResponseTx<()>>),
TimerKeyRotation(ApiCall<WireguardData>),
OneshotKeyRotation(ApiCall<WireguardData>),
Validation(ApiCall<Device>),
@@ -119,8 +117,8 @@ impl futures::Future for Call {
}
}
-pub enum ApiResult {
- Login(Result<DeviceData, Error>, ResponseTx<()>),
+pub(crate) enum ApiResult {
+ Login(Result<PrivateAccountAndDevice, Error>, ResponseTx<()>),
Rotation(Result<WireguardData, Error>),
Validation(Result<Device, Error>),
}
diff --git a/mullvad-daemon/src/device/mod.rs b/mullvad-daemon/src/device/mod.rs
index 158330f15a..753c963cca 100644
--- a/mullvad-daemon/src/device/mod.rs
+++ b/mullvad-daemon/src/device/mod.rs
@@ -7,8 +7,8 @@ use futures::{
use mullvad_api::{availability::ApiAvailabilityHandle, rest};
use mullvad_types::{
account::AccountToken,
- device::{Device, DeviceData, DeviceEvent, InnerDevice},
- wireguard::{RotationInterval, WireguardData},
+ device::{AccountAndDevice, Device, DeviceEvent, DeviceId, DeviceName, DevicePort},
+ wireguard::{self, RotationInterval, WireguardData},
};
use std::{
future::Future,
@@ -28,7 +28,7 @@ use tokio::{
mod api;
mod service;
-pub use service::{spawn_account_service, AccountService, DeviceService};
+pub(crate) use service::{AccountService, DeviceService};
/// File that used to store account and device data.
const DEVICE_CACHE_FILENAME: &str = "device.json";
@@ -70,39 +70,112 @@ pub enum Error {
AccountManagerDown,
}
+/// Same as [PrivateDevice] but also contains the associated account token.
+#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
+pub struct PrivateAccountAndDevice {
+ pub account_token: AccountToken,
+ pub device: PrivateDevice,
+}
+
+impl From<PrivateAccountAndDevice> for AccountAndDevice {
+ fn from(config: PrivateAccountAndDevice) -> Self {
+ AccountAndDevice {
+ account_token: config.account_token,
+ device: Device::from(config.device),
+ }
+ }
+}
+
+/// Device type that contains private data.
+#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
+pub struct PrivateDevice {
+ pub id: DeviceId,
+ pub name: DeviceName,
+ pub wg_data: wireguard::WireguardData,
+ pub ports: Vec<DevicePort>,
+}
+
+impl PrivateDevice {
+ /// Construct a private device from a `WireguardData` and a `Device`. Fails if the pubkey of
+ /// `device` does not match that of `wg_data`.
+ pub fn try_from_device(
+ device: Device,
+ wg_data: wireguard::WireguardData,
+ ) -> Result<Self, Error> {
+ if device.pubkey != wg_data.private_key.public_key() {
+ return Err(Error::InvalidDevice);
+ }
+ Ok(Self {
+ id: device.id,
+ name: device.name,
+ wg_data,
+ ports: device.ports,
+ })
+ }
+
+ /// Update all device details that are present in both types. Fails if the pubkey of `device`
+ /// does not match that of `wg_data`.
+ fn update(&mut self, device: Device) -> Result<(), Error> {
+ if device.pubkey != self.wg_data.private_key.public_key() {
+ return Err(Error::InvalidDevice);
+ }
+ self.id = device.id;
+ self.ports = device.ports;
+ self.name = device.name;
+ Ok(())
+ }
+}
+
+impl From<PrivateDevice> for Device {
+ fn from(device: PrivateDevice) -> Self {
+ Device {
+ id: device.id,
+ ports: device.ports,
+ pubkey: device.wg_data.private_key.public_key(),
+ name: device.name,
+ }
+ }
+}
+
#[derive(Clone)]
-pub(crate) enum InnerDeviceEvent {
+pub(crate) enum PrivateDeviceEvent {
/// The device was removed due to user (or daemon) action.
Logout,
/// Logged in to a new device.
- Login(DeviceData),
+ Login(PrivateAccountAndDevice),
/// The device was updated remotely, but not its key.
- Updated(DeviceData),
+ Updated(PrivateAccountAndDevice),
/// The key was rotated.
- RotatedKey(DeviceData),
+ RotatedKey(PrivateAccountAndDevice),
/// Device was removed because it was not found remotely.
Revoked,
}
-impl From<InnerDeviceEvent> for DeviceEvent {
- fn from(event: InnerDeviceEvent) -> DeviceEvent {
+impl From<PrivateDeviceEvent> for DeviceEvent {
+ fn from(event: PrivateDeviceEvent) -> DeviceEvent {
match event {
- InnerDeviceEvent::Logout => DeviceEvent::revoke(false),
- InnerDeviceEvent::Login(data) => DeviceEvent::from_device(data, false),
- InnerDeviceEvent::Updated(data) => DeviceEvent::from_device(data, true),
- InnerDeviceEvent::RotatedKey(data) => DeviceEvent::from_device(data, false),
- InnerDeviceEvent::Revoked => DeviceEvent::revoke(true),
+ PrivateDeviceEvent::Logout => DeviceEvent::revoke(false),
+ PrivateDeviceEvent::Login(config) => {
+ DeviceEvent::from_device(AccountAndDevice::from(config), false)
+ }
+ PrivateDeviceEvent::Updated(config) => {
+ DeviceEvent::from_device(AccountAndDevice::from(config), true)
+ }
+ PrivateDeviceEvent::RotatedKey(config) => {
+ DeviceEvent::from_device(AccountAndDevice::from(config), false)
+ }
+ PrivateDeviceEvent::Revoked => DeviceEvent::revoke(true),
}
}
}
-impl InnerDeviceEvent {
- fn data(&self) -> Option<&DeviceData> {
+impl PrivateDeviceEvent {
+ fn data(&self) -> Option<&PrivateAccountAndDevice> {
match self {
- InnerDeviceEvent::Login(data) => Some(&data),
- InnerDeviceEvent::Updated(data) => Some(&data),
- InnerDeviceEvent::RotatedKey(data) => Some(&data),
- InnerDeviceEvent::Logout | InnerDeviceEvent::Revoked => None,
+ PrivateDeviceEvent::Login(config) => Some(config),
+ PrivateDeviceEvent::Updated(config) => Some(config),
+ PrivateDeviceEvent::RotatedKey(config) => Some(config),
+ PrivateDeviceEvent::Logout | PrivateDeviceEvent::Revoked => None,
}
}
}
@@ -122,13 +195,13 @@ type ResponseTx<T> = oneshot::Sender<Result<T, Error>>;
enum AccountManagerCommand {
Login(AccountToken, ResponseTx<()>),
Logout(ResponseTx<()>),
- SetData(DeviceData, ResponseTx<()>),
- GetData(ResponseTx<Option<DeviceData>>),
- GetDataAfterLogin(ResponseTx<Option<DeviceData>>),
+ SetData(PrivateAccountAndDevice, ResponseTx<()>),
+ GetData(ResponseTx<Option<PrivateAccountAndDevice>>),
+ GetDataAfterLogin(ResponseTx<Option<PrivateAccountAndDevice>>),
RotateKey(ResponseTx<()>),
SetRotationInterval(RotationInterval, ResponseTx<()>),
ValidateDevice(ResponseTx<()>),
- ReceiveEvents(Box<dyn Sender<InnerDeviceEvent> + Send>, ResponseTx<()>),
+ ReceiveEvents(Box<dyn Sender<PrivateDeviceEvent> + Send>, ResponseTx<()>),
Shutdown(oneshot::Sender<()>),
}
@@ -150,17 +223,17 @@ impl AccountManagerHandle {
.await
}
- pub async fn set(&self, data: DeviceData) -> Result<(), Error> {
+ pub async fn set(&self, data: PrivateAccountAndDevice) -> Result<(), Error> {
self.send_command(|tx| AccountManagerCommand::SetData(data, tx))
.await
}
- pub async fn data(&self) -> Result<Option<DeviceData>, Error> {
+ pub async fn data(&self) -> Result<Option<PrivateAccountAndDevice>, Error> {
self.send_command(|tx| AccountManagerCommand::GetData(tx))
.await
}
- pub async fn data_after_login(&self) -> Result<Option<DeviceData>, Error> {
+ pub async fn data_after_login(&self) -> Result<Option<PrivateAccountAndDevice>, Error> {
self.send_command(|tx| AccountManagerCommand::GetDataAfterLogin(tx))
.await
}
@@ -182,7 +255,7 @@ impl AccountManagerHandle {
pub async fn receive_events(
&self,
- events_tx: impl Sender<InnerDeviceEvent> + Send + 'static,
+ events_tx: impl Sender<PrivateDeviceEvent> + Send + 'static,
) -> Result<(), Error> {
self.send_command(|tx| {
AccountManagerCommand::ReceiveEvents(Box::new(events_tx) as Box<_>, tx)
@@ -213,13 +286,13 @@ impl AccountManagerHandle {
pub(crate) struct AccountManager {
cacher: DeviceCacher,
device_service: DeviceService,
- data: Option<DeviceData>,
+ data: Option<PrivateAccountAndDevice>,
rotation_interval: RotationInterval,
- listeners: Vec<Box<dyn Sender<InnerDeviceEvent> + Send>>,
+ listeners: Vec<Box<dyn Sender<PrivateDeviceEvent> + Send>>,
last_validation: Option<SystemTime>,
validation_requests: Vec<ResponseTx<()>>,
rotation_requests: Vec<ResponseTx<()>>,
- data_requests: Vec<ResponseTx<Option<DeviceData>>>,
+ data_requests: Vec<ResponseTx<Option<PrivateAccountAndDevice>>>,
}
impl AccountManager {
@@ -230,9 +303,9 @@ impl AccountManager {
initial_rotation_interval: RotationInterval,
) -> Result<AccountManagerHandle, Error> {
let (cacher, data) = DeviceCacher::new(settings_dir).await?;
- let token = data.as_ref().map(|state| state.token.clone());
+ let token = data.as_ref().map(|state| state.account_token.clone());
let account_service =
- spawn_account_service(rest_handle.clone(), token, api_availability.clone());
+ service::spawn_account_service(rest_handle.clone(), token, api_availability.clone());
let (cmd_tx, cmd_rx) = mpsc::unbounded();
@@ -284,7 +357,7 @@ impl AccountManager {
self.logout(tx).await;
}
Some(AccountManagerCommand::SetData(data, tx)) => {
- let _ = tx.send(self.set(InnerDeviceEvent::Login(data)).await);
+ let _ = tx.send(self.set(PrivateDeviceEvent::Login(data)).await);
}
Some(AccountManagerCommand::GetData(tx)) => {
let _ = tx.send(Ok(self.data.clone()));
@@ -393,10 +466,11 @@ impl AccountManager {
async fn consume_login(
&mut self,
- device_response: Result<DeviceData, Error>,
+ device_response: Result<PrivateAccountAndDevice, Error>,
tx: ResponseTx<()>,
) {
- let _ = tx.send(async { self.set(InnerDeviceEvent::Login(device_response?)).await }.await);
+ let _ =
+ tx.send(async { self.set(PrivateDeviceEvent::Login(device_response?)).await }.await);
let data = self.data.clone();
Self::drain_requests(&mut self.data_requests, || Ok(data.clone()));
}
@@ -406,7 +480,7 @@ impl AccountManager {
response: Result<Device, Error>,
api_call: &mut api::CurrentApiCall,
) {
- let current_data = match self.data.as_ref() {
+ let current_config = match self.data.as_ref() {
Some(data) => data,
None => {
panic!("Received a validation response whilst having no device data");
@@ -414,13 +488,14 @@ impl AccountManager {
};
match response {
- Ok(new_device_data) => {
- let current_pubkey = current_data.wg_data.private_key.public_key();
- if new_device_data.pubkey == current_pubkey {
- let new_data = DeviceData {
- device: InnerDevice::from(new_device_data),
- ..current_data.clone()
- };
+ Ok(new_device) => {
+ let current_pubkey = current_config.device.wg_data.private_key.public_key();
+ if new_device.pubkey == current_pubkey {
+ let mut new_data = current_config.clone();
+ new_data
+ .device
+ .update(new_device)
+ .expect("pubkey must match privkey");
if Some(&new_data) != self.data.as_ref() {
log::debug!("Updating data for the current device");
@@ -428,7 +503,7 @@ impl AccountManager {
log::debug!("The current device is still valid");
}
- match self.set(InnerDeviceEvent::Updated(new_data)).await {
+ match self.set(PrivateDeviceEvent::Updated(new_data)).await {
Ok(_) => {
Self::drain_requests(&mut self.validation_requests, || Ok(()));
}
@@ -460,10 +535,10 @@ impl AccountManager {
}
if !self.rotation_requests.is_empty() || !self.validation_requests.is_empty() {
- if let Some(updated_data) = self.data.as_ref() {
+ if let Some(updated_config) = self.data.as_ref() {
let device_service = self.device_service.clone();
- let token = updated_data.token.clone();
- let device_id = updated_data.device.id.clone();
+ let token = updated_config.account_token.clone();
+ let device_id = updated_config.device.id.clone();
api_call.set_oneshot_rotation(Box::pin(async move {
device_service.rotate_key(token, device_id).await
}));
@@ -472,15 +547,15 @@ impl AccountManager {
}
async fn consume_rotation_result(&mut self, api_result: Result<WireguardData, Error>) {
- let mut device_data = self
+ let mut config = self
.data
.clone()
.expect("Received a key rotation result whilst having no data");
match api_result {
Ok(wg_data) => {
- device_data.wg_data = wg_data;
- match self.set(InnerDeviceEvent::RotatedKey(device_data)).await {
+ config.device.wg_data = wg_data;
+ match self.set(PrivateDeviceEvent::RotatedKey(config)).await {
Ok(_) => {
Self::drain_requests(&mut self.rotation_requests, || Ok(()));
@@ -522,12 +597,12 @@ impl AccountManager {
fn spawn_timed_key_rotation(
&self,
) -> Option<impl Future<Output = Result<WireguardData, Error>> + Send + 'static> {
- let data = self.data.as_ref()?;
- let key_rotation_timer = self.key_rotation_timer(data.wg_data.created);
+ let config = self.data.as_ref()?;
+ let key_rotation_timer = self.key_rotation_timer(config.device.wg_data.created);
let device_service = self.device_service.clone();
- let account_token = data.token.clone();
- let device_id = data.device.id.clone();
+ let account_token = config.account_token.clone();
+ let device_id = config.device.id.clone();
Some(async move {
key_rotation_timer.await;
@@ -550,7 +625,7 @@ impl AccountManager {
Self::drain_requests(&mut self.rotation_requests, || Err(err_constructor()));
self.listeners
- .retain(|listener| listener.send(InnerDeviceEvent::Revoked).is_ok());
+ .retain(|listener| listener.send(PrivateDeviceEvent::Revoked).is_ok());
}
async fn logout(&mut self, tx: ResponseTx<()>) {
@@ -565,12 +640,12 @@ impl AccountManager {
}
// Cannot panic: `data.is_none() == false`.
- let old_data = self.data.take().unwrap();
+ let old_config = self.data.take().unwrap();
self.listeners
- .retain(|listener| listener.send(InnerDeviceEvent::Logout).is_ok());
+ .retain(|listener| listener.send(PrivateDeviceEvent::Logout).is_ok());
- let logout_call = tokio::spawn(Box::pin(self.logout_api_call(old_data)));
+ let logout_call = tokio::spawn(Box::pin(self.logout_api_call(old_config)));
tokio::spawn(async move {
let _response = tokio::time::timeout(LOGOUT_TIMEOUT, logout_call).await;
@@ -578,12 +653,12 @@ impl AccountManager {
});
}
- fn logout_api_call(&self, data: DeviceData) -> impl Future<Output = ()> + 'static {
+ fn logout_api_call(&self, data: PrivateAccountAndDevice) -> impl Future<Output = ()> + 'static {
let service = self.device_service.clone();
async move {
if let Err(error) = service
- .remove_device_with_backoff(data.token, data.device.id)
+ .remove_device_with_backoff(data.account_token, data.device.id)
.await
{
log::error!(
@@ -594,7 +669,7 @@ impl AccountManager {
}
}
- async fn set(&mut self, event: InnerDeviceEvent) -> Result<(), Error> {
+ async fn set(&mut self, event: PrivateDeviceEvent) -> Result<(), Error> {
let data = event.data();
if data == self.data.as_ref() {
return Ok(());
@@ -603,9 +678,9 @@ impl AccountManager {
self.cacher.write(data).await?;
self.last_validation = None;
- if let Some(old_data) = self.data.take() {
- if data.as_ref().map(|d| &d.device.id) != Some(&old_data.device.id) {
- tokio::spawn(self.logout_api_call(old_data));
+ if let Some(old_config) = self.data.take() {
+ if data.as_ref().map(|d| &d.device.id) != Some(&old_config.device.id) {
+ tokio::spawn(self.logout_api_call(old_config));
}
}
@@ -622,7 +697,11 @@ impl AccountManager {
) -> Result<impl Future<Output = Result<WireguardData, Error>>, Error> {
let data = self.data.clone().ok_or(Error::NoDevice)?;
let device_service = self.device_service.clone();
- Ok(async move { device_service.rotate_key(data.token, data.device.id).await })
+ Ok(async move {
+ device_service
+ .rotate_key(data.account_token, data.device.id)
+ .await
+ })
}
fn key_rotation_timer(&self, key_created: DateTime<Utc>) -> impl Future<Output = ()> + 'static {
@@ -652,19 +731,19 @@ impl AccountManager {
}
}
- fn fetch_device_data(
+ fn fetch_device_config(
&self,
- old_data: &DeviceData,
+ old_config: &PrivateAccountAndDevice,
) -> impl Future<Output = Result<Device, Error>> {
let device_service = self.device_service.clone();
- let account_token = old_data.token.clone();
- let device_id = old_data.device.id.clone();
+ let account_token = old_config.account_token.clone();
+ let device_id = old_config.device.id.clone();
async move { device_service.get(account_token, device_id).await }
}
fn validation_call(&self) -> Result<impl Future<Output = Result<Device, Error>>, Error> {
- let old_data = self.data.as_ref().ok_or(Error::NoDevice)?;
- let device_request = self.fetch_device_data(old_data);
+ let old_config = self.data.as_ref().ok_or(Error::NoDevice)?;
+ let device_request = self.fetch_device_config(old_config);
Ok(async move { device_request.await })
}
@@ -698,7 +777,9 @@ pub struct DeviceCacher {
}
impl DeviceCacher {
- pub async fn new(settings_dir: &Path) -> Result<(DeviceCacher, Option<DeviceData>), Error> {
+ pub async fn new(
+ settings_dir: &Path,
+ ) -> Result<(DeviceCacher, Option<PrivateAccountAndDevice>), Error> {
let path = settings_dir.join(DEVICE_CACHE_FILENAME);
let cache_exists = path.is_file();
@@ -709,7 +790,7 @@ impl DeviceCacher {
.open(&path)
.await?;
- let device: Option<DeviceData> = if cache_exists {
+ let device: Option<PrivateAccountAndDevice> = if cache_exists {
let mut reader = io::BufReader::new(&mut file);
let mut buffer = String::new();
reader.read_to_string(&mut buffer).await?;
@@ -753,7 +834,7 @@ impl DeviceCacher {
options
}
- pub async fn write(&mut self, device: Option<&DeviceData>) -> Result<(), Error> {
+ pub async fn write(&mut self, device: Option<&PrivateAccountAndDevice>) -> Result<(), Error> {
let data = serde_json::to_vec_pretty(&device).unwrap();
self.file.get_mut().set_len(0).await?;
diff --git a/mullvad-daemon/src/device/service.rs b/mullvad-daemon/src/device/service.rs
index 7f39bfc145..f511a35f67 100644
--- a/mullvad-daemon/src/device/service.rs
+++ b/mullvad-daemon/src/device/service.rs
@@ -4,12 +4,12 @@ use chrono::{DateTime, Utc};
use futures::future::{abortable, AbortHandle};
use mullvad_types::{
account::{AccountToken, VoucherSubmission},
- device::{Device, DeviceData, DeviceId, InnerDevice},
+ device::{Device, DeviceId},
wireguard::WireguardData,
};
use talpid_types::net::wireguard::PrivateKey;
-use super::Error;
+use super::{Error, PrivateAccountAndDevice, PrivateDevice};
use mullvad_api::{
availability::ApiAvailabilityHandle,
rest::{self, Error as RestError, MullvadRestHandle},
@@ -42,14 +42,14 @@ impl DeviceService {
/// Generate a new device for a given token
pub fn generate_for_account(
&self,
- token: AccountToken,
- ) -> impl Future<Output = Result<DeviceData, Error>> + Send {
+ account_token: AccountToken,
+ ) -> impl Future<Output = Result<PrivateAccountAndDevice, Error>> + Send {
let private_key = PrivateKey::new_from_random();
let pubkey = private_key.public_key();
let proxy = self.proxy.clone();
let api_handle = self.api_availability.clone();
- let token_copy = token.clone();
+ let token_copy = account_token.clone();
async move {
let (device, addresses) = retry_future_n(
move || proxy.create(token_copy.clone(), pubkey.clone()),
@@ -60,28 +60,30 @@ impl DeviceService {
.await
.map_err(map_rest_error)?;
- Ok(DeviceData {
- token,
- device: InnerDevice::from(device),
- wg_data: WireguardData {
- private_key,
- addresses,
- created: Utc::now(),
- },
+ Ok(PrivateAccountAndDevice {
+ account_token,
+ device: PrivateDevice::try_from_device(
+ device,
+ WireguardData {
+ private_key,
+ addresses,
+ created: Utc::now(),
+ },
+ )?,
})
}
}
pub async fn generate_for_account_with_backoff(
&self,
- token: AccountToken,
- ) -> Result<DeviceData, Error> {
+ account_token: AccountToken,
+ ) -> Result<PrivateAccountAndDevice, Error> {
let private_key = PrivateKey::new_from_random();
let pubkey = private_key.public_key();
let proxy = self.proxy.clone();
let api_handle = self.api_availability.clone();
- let token_copy = token.clone();
+ let token_copy = account_token.clone();
let (device, addresses) = retry_future(
move || api_handle.when_online(proxy.create(token_copy.clone(), pubkey.clone())),
should_retry_backoff,
@@ -90,14 +92,16 @@ impl DeviceService {
.await
.map_err(map_rest_error)?;
- Ok(DeviceData {
- token,
- device: InnerDevice::from(device),
- wg_data: WireguardData {
- private_key,
- addresses,
- created: Utc::now(),
- },
+ Ok(PrivateAccountAndDevice {
+ account_token,
+ device: PrivateDevice::try_from_device(
+ device,
+ WireguardData {
+ private_key,
+ addresses,
+ created: Utc::now(),
+ },
+ )?,
})
}
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index eb674b5763..04300018fd 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -25,7 +25,7 @@ pub mod version;
mod version_check;
use crate::target_state::PersistentTargetState;
-use device::InnerDeviceEvent;
+use device::{PrivateAccountAndDevice, PrivateDeviceEvent};
use futures::{
channel::{mpsc, oneshot},
future::{abortable, AbortHandle, Future},
@@ -38,7 +38,7 @@ use mullvad_relay_selector::{
};
use mullvad_types::{
account::{AccountData, AccountToken, VoucherSubmission},
- device::{Device, DeviceConfig, DeviceData, DeviceEvent, DeviceId, RemoveDeviceEvent},
+ device::{AccountAndDevice, Device, DeviceEvent, DeviceId, RemoveDeviceEvent},
endpoint::MullvadEndpoint,
location::GeoIpLocation,
relay_constraints::{BridgeSettings, BridgeState, ObfuscationSettings, RelaySettingsUpdate},
@@ -236,7 +236,7 @@ pub enum DaemonCommand {
/// Log out of the current account and remove the device, if they exist.
LogoutAccount(ResponseTx<(), Error>),
/// Return the current device configuration, if there is one.
- GetDevice(ResponseTx<Option<DeviceConfig>, Error>),
+ GetDevice(ResponseTx<Option<AccountAndDevice>, Error>),
/// Update/check the current device, if there is one.
UpdateDevice(ResponseTx<(), Error>),
/// Return all the devices for a given account token.
@@ -340,9 +340,9 @@ pub(crate) enum InternalDaemonEvent {
/// The background job fetching new `AppVersionInfo`s got a new info object.
NewAppVersionInfo(AppVersionInfo),
/// Sent when a device is updated in any way (key rotation, login, logout, etc.).
- DeviceEvent(InnerDeviceEvent),
+ DeviceEvent(PrivateDeviceEvent),
/// Handles updates from versions without devices.
- DeviceMigrationEvent(Result<DeviceData, device::Error>),
+ DeviceMigrationEvent(Result<PrivateAccountAndDevice, device::Error>),
/// The split tunnel paths or state were updated.
#[cfg(target_os = "windows")]
ExcludedPathsEvent(ExcludedPathsUpdate, oneshot::Sender<Result<(), Error>>),
@@ -372,8 +372,8 @@ impl From<AppVersionInfo> for InternalDaemonEvent {
}
}
-impl From<InnerDeviceEvent> for InternalDaemonEvent {
- fn from(event: InnerDeviceEvent) -> Self {
+impl From<PrivateDeviceEvent> for InternalDaemonEvent {
+ fn from(event: PrivateDeviceEvent) -> Self {
InternalDaemonEvent::DeviceEvent(event)
}
}
@@ -665,7 +665,7 @@ where
let account_history = account_history::AccountHistory::new(
&settings_dir,
- data.as_ref().map(|device| device.token.clone()),
+ data.as_ref().map(|device| device.account_token.clone()),
)
.await
.map_err(Error::LoadAccountHistory)?;
@@ -1032,7 +1032,7 @@ where
endpoint: MullvadEndpoint,
bridge: Option<SelectedBridge>,
obfuscator: Option<SelectedObfuscator>,
- device: DeviceData,
+ device: PrivateAccountAndDevice,
) -> Result<TunnelParameters, Error> {
let tunnel_options = self.settings.tunnel_options.clone();
match endpoint {
@@ -1052,7 +1052,11 @@ where
});
Ok(openvpn::TunnelParameters {
- config: openvpn::ConnectionConfig::new(endpoint, device.token, "-".to_string()),
+ config: openvpn::ConnectionConfig::new(
+ endpoint,
+ device.account_token,
+ "-".to_string(),
+ ),
options: tunnel_options.openvpn,
generic_options: tunnel_options.generic,
proxy: bridge_settings,
@@ -1065,10 +1069,10 @@ where
}
MullvadEndpoint::Wireguard(endpoint) => {
let tunnel = wireguard::TunnelConfig {
- private_key: device.wg_data.private_key,
+ private_key: device.device.wg_data.private_key,
addresses: vec![
- device.wg_data.addresses.ipv4_address.ip().into(),
- device.wg_data.addresses.ipv6_address.ip().into(),
+ device.device.wg_data.addresses.ipv4_address.ip().into(),
+ device.device.wg_data.addresses.ipv6_address.ip().into(),
],
};
@@ -1217,10 +1221,10 @@ where
self.event_listener.notify_app_version(app_version_info);
}
- async fn handle_device_event(&mut self, event: InnerDeviceEvent) {
+ async fn handle_device_event(&mut self, event: PrivateDeviceEvent) {
match &event {
- InnerDeviceEvent::Login(device) => {
- if let Err(error) = self.account_history.set(device.token.clone()).await {
+ PrivateDeviceEvent::Login(device) => {
+ if let Err(error) = self.account_history.set(device.account_token.clone()).await {
log::error!(
"{}",
error.display_chain_with_msg("Failed to update account history")
@@ -1231,18 +1235,18 @@ where
self.reconnect_tunnel();
}
}
- InnerDeviceEvent::Logout => {
+ PrivateDeviceEvent::Logout => {
log::info!("Disconnecting because account token was cleared");
self.set_target_state(TargetState::Unsecured).await;
}
- InnerDeviceEvent::Revoked => {
+ PrivateDeviceEvent::Revoked => {
// If we're currently in a secured state, reconnect to make sure we immediately
// enter the error state.
if *self.target_state == TargetState::Secured {
self.connect_tunnel();
}
}
- InnerDeviceEvent::RotatedKey(_) => {
+ PrivateDeviceEvent::RotatedKey(_) => {
if let Some(TunnelType::Wireguard) = self.get_target_tunnel_type() {
self.schedule_reconnect(WG_RECONNECT_DELAY);
}
@@ -1253,7 +1257,10 @@ where
.notify_device_event(DeviceEvent::from(event));
}
- async fn handle_device_migration_event(&mut self, result: Result<DeviceData, device::Error>) {
+ async fn handle_device_migration_event(
+ &mut self,
+ result: Result<PrivateAccountAndDevice, device::Error>,
+ ) {
let account_manager = self.account_manager.clone();
let event_listener = self.event_listener.clone();
tokio::spawn(async move {
@@ -1479,7 +1486,7 @@ where
let future = self
.account_manager
.account_service
- .get_www_auth_token(device.token);
+ .get_www_auth_token(device.account_token);
tokio::spawn(async {
Self::oneshot_send(
tx,
@@ -1507,7 +1514,7 @@ where
Self::oneshot_send(
tx,
account
- .submit_voucher(device.token, voucher)
+ .submit_voucher(device.account_token, voucher)
.await
.map_err(Error::RestError),
"submit_voucher response",
@@ -1552,7 +1559,7 @@ where
});
}
- async fn on_get_device(&mut self, tx: ResponseTx<Option<DeviceConfig>, Error>) {
+ async fn on_get_device(&mut self, tx: ResponseTx<Option<AccountAndDevice>, Error>) {
let account_manager = self.account_manager.clone();
tokio::spawn(async move {
Self::oneshot_send(
@@ -1561,7 +1568,7 @@ where
.data()
.await
.unwrap_or(None)
- .map(DeviceConfig::from)),
+ .map(AccountAndDevice::from)),
"get_device response",
);
});
@@ -2290,8 +2297,8 @@ where
}
async fn on_get_wireguard_key(&self, tx: ResponseTx<Option<PublicKey>, Error>) {
- let result = if let Ok(Some(device)) = self.account_manager.data().await {
- Ok(Some(device.wg_data.get_public_key()))
+ let result = if let Ok(Some(config)) = self.account_manager.data().await {
+ Ok(Some(config.device.wg_data.get_public_key()))
} else {
Err(Error::NoAccountToken)
};
diff --git a/mullvad-daemon/src/migrations/device.rs b/mullvad-daemon/src/migrations/device.rs
index 6420d5df51..ff6cb16379 100644
--- a/mullvad-daemon/src/migrations/device.rs
+++ b/mullvad-daemon/src/migrations/device.rs
@@ -8,14 +8,10 @@
use super::{v5::MigrationData, MigrationComplete};
use crate::{
- device::{self, DeviceService},
+ device::{self, DeviceService, PrivateAccountAndDevice, PrivateDevice},
DaemonEventSender, InternalDaemonEvent,
};
-use mullvad_types::{
- account::AccountToken,
- device::{DeviceData, InnerDevice},
- wireguard::WireguardData,
-};
+use mullvad_types::{account::AccountToken, wireguard::WireguardData};
use std::time::Duration;
use talpid_core::mpsc::Sender;
use talpid_types::ErrorExt;
@@ -61,51 +57,56 @@ pub(crate) fn generate_device(
async fn cache_from_wireguard_key(
service: DeviceService,
- token: AccountToken,
+ account_token: AccountToken,
wg_data: WireguardData,
-) -> Result<DeviceData, device::Error> {
- let devices = timeout(TIMEOUT, service.list_devices_with_backoff(token.clone()))
- .await
- .map_err(|_error| {
- log::error!("Failed to enumerate devices for account: timed out");
- device::Error::Cancelled
- })?
- .map_err(|error| {
- log::error!(
- "{}",
- error.display_chain_with_msg("Failed to enumerate devices for account")
- );
- error
- })?;
+) -> Result<PrivateAccountAndDevice, device::Error> {
+ let devices = timeout(
+ TIMEOUT,
+ service.list_devices_with_backoff(account_token.clone()),
+ )
+ .await
+ .map_err(|_error| {
+ log::error!("Failed to enumerate devices for account: timed out");
+ device::Error::Cancelled
+ })?
+ .map_err(|error| {
+ log::error!(
+ "{}",
+ error.display_chain_with_msg("Failed to enumerate devices for account")
+ );
+ error
+ })?;
for device in devices.into_iter() {
if device.pubkey == wg_data.private_key.public_key() {
- return Ok(DeviceData {
- token,
- device: InnerDevice::from(device),
- wg_data,
+ return Ok(PrivateAccountAndDevice {
+ account_token,
+ device: PrivateDevice::try_from_device(device, wg_data)?,
});
}
}
log::info!("The existing WireGuard key is not valid; generating a new device");
- cache_from_account(service, token).await
+ cache_from_account(service, account_token).await
}
async fn cache_from_account(
service: DeviceService,
- token: AccountToken,
-) -> Result<DeviceData, device::Error> {
- timeout(TIMEOUT, service.generate_for_account_with_backoff(token))
- .await
- .map_err(|_error| {
- log::error!("Failed to generate new device for account: timed out");
- device::Error::Cancelled
- })?
- .map_err(|error| {
- log::error!(
- "{}",
- error.display_chain_with_msg("Failed to generate new device for account")
- );
- error
- })
+ account_token: AccountToken,
+) -> Result<PrivateAccountAndDevice, device::Error> {
+ timeout(
+ TIMEOUT,
+ service.generate_for_account_with_backoff(account_token),
+ )
+ .await
+ .map_err(|_error| {
+ log::error!("Failed to generate new device for account: timed out");
+ device::Error::Cancelled
+ })?
+ .map_err(|error| {
+ log::error!(
+ "{}",
+ error.display_chain_with_msg("Failed to generate new device for account")
+ );
+ error
+ })
}
diff --git a/mullvad-jni/src/classes.rs b/mullvad-jni/src/classes.rs
index 3aceb3a820..a453b4ad6c 100644
--- a/mullvad-jni/src/classes.rs
+++ b/mullvad-jni/src/classes.rs
@@ -3,13 +3,13 @@ pub const CLASSES: &[&str] = &[
"java/net/InetAddress",
"java/net/InetSocketAddress",
"java/util/ArrayList",
+ "net/mullvad/mullvadvpn/model/AccountAndDevice",
"net/mullvad/mullvadvpn/model/AccountData",
"net/mullvad/mullvadvpn/model/AppVersionInfo",
"net/mullvad/mullvadvpn/model/Constraint$Any",
"net/mullvad/mullvadvpn/model/Constraint$Only",
"net/mullvad/mullvadvpn/model/DnsOptions",
"net/mullvad/mullvadvpn/model/Device",
- "net/mullvad/mullvadvpn/model/DeviceConfig",
"net/mullvad/mullvadvpn/model/DeviceEvent",
"net/mullvad/mullvadvpn/model/RemoveDeviceEvent",
"net/mullvad/mullvadvpn/model/GeoIpLocation",
diff --git a/mullvad-jni/src/daemon_interface.rs b/mullvad-jni/src/daemon_interface.rs
index ae774ae8ee..f429201e88 100644
--- a/mullvad-jni/src/daemon_interface.rs
+++ b/mullvad-jni/src/daemon_interface.rs
@@ -2,7 +2,7 @@ use futures::{channel::oneshot, executor::block_on};
use mullvad_daemon::{device, DaemonCommand, DaemonCommandSender};
use mullvad_types::{
account::{AccountData, AccountToken, VoucherSubmission},
- device::{Device, DeviceConfig},
+ device::{AccountAndDevice, Device},
location::GeoIpLocation,
relay_constraints::RelaySettingsUpdate,
relay_list::RelayList,
@@ -212,7 +212,7 @@ impl DaemonInterface {
.map_err(Error::from)
}
- pub fn get_device(&self) -> Result<Option<DeviceConfig>> {
+ pub fn get_device(&self) -> Result<Option<AccountAndDevice>> {
let (tx, rx) = oneshot::channel();
self.send_command(DaemonCommand::GetDevice(tx))?;
diff --git a/mullvad-management-interface/src/types.rs b/mullvad-management-interface/src/types.rs
index 13721066a5..d685899256 100644
--- a/mullvad-management-interface/src/types.rs
+++ b/mullvad-management-interface/src/types.rs
@@ -231,7 +231,7 @@ impl From<mullvad_types::device::DeviceEvent> for DeviceEvent {
fn from(event: mullvad_types::device::DeviceEvent) -> Self {
DeviceEvent {
device: event.device.map(|config| DeviceConfig {
- account_token: config.token,
+ account_token: config.account_token,
device: Some(Device::from(config.device)),
}),
remote: event.remote,
@@ -249,10 +249,10 @@ impl From<mullvad_types::device::RemoveDeviceEvent> for RemoveDeviceEvent {
}
}
-impl From<mullvad_types::device::DeviceConfig> for DeviceConfig {
- fn from(device: mullvad_types::device::DeviceConfig) -> Self {
+impl From<mullvad_types::device::AccountAndDevice> for DeviceConfig {
+ fn from(device: mullvad_types::device::AccountAndDevice) -> Self {
DeviceConfig {
- account_token: device.token,
+ account_token: device.account_token,
device: Some(Device::from(device.device)),
}
}
diff --git a/mullvad-setup/src/main.rs b/mullvad-setup/src/main.rs
index 10ebb36981..15a0c7dd6d 100644
--- a/mullvad-setup/src/main.rs
+++ b/mullvad-setup/src/main.rs
@@ -183,7 +183,7 @@ async fn remove_device() -> Result<(), Error> {
.await,
);
retry_future_n(
- move || proxy.remove(device.token.clone(), device.device.id.clone()),
+ move || proxy.remove(device.account_token.clone(), device.device.id.clone()),
move |result| match result {
Err(error) => error.is_network_error(),
_ => false,
diff --git a/mullvad-types/src/device.rs b/mullvad-types/src/device.rs
index 0a0dd4d91d..93836c674a 100644
--- a/mullvad-types/src/device.rs
+++ b/mullvad-types/src/device.rs
@@ -1,4 +1,4 @@
-use crate::{account::AccountToken, wireguard};
+use crate::account::AccountToken;
#[cfg(target_os = "android")]
use jnix::IntoJava;
use serde::{Deserialize, Serialize};
@@ -61,57 +61,20 @@ impl fmt::Display for DevicePort {
}
}
-/// A complete device configuration.
-#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
-pub struct DeviceData {
- pub token: AccountToken,
- pub device: InnerDevice,
- pub wg_data: wireguard::WireguardData,
-}
-
-impl From<DeviceData> for Device {
- fn from(data: DeviceData) -> Device {
- Device {
- id: data.device.id,
- name: data.device.name,
- pubkey: data.wg_data.private_key.public_key(),
- ports: data.device.ports,
- }
- }
-}
-
-/// Device data used by [DeviceData].
-#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
-pub struct InnerDevice {
- pub id: DeviceId,
- pub name: DeviceName,
- pub ports: Vec<DevicePort>,
-}
-
-impl From<Device> for InnerDevice {
- fn from(device: Device) -> Self {
- InnerDevice {
- id: device.id,
- name: device.name,
- ports: device.ports,
- }
- }
-}
-
-/// [`DeviceData`] excluding the private key.
+/// A [Device] and its associated account token.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[cfg_attr(target_os = "android", derive(IntoJava))]
#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))]
-pub struct DeviceConfig {
- pub token: AccountToken,
+pub struct AccountAndDevice {
+ pub account_token: AccountToken,
pub device: Device,
}
-impl From<DeviceData> for DeviceConfig {
- fn from(data: DeviceData) -> DeviceConfig {
- DeviceConfig {
- token: data.token.clone(),
- device: Device::from(data),
+impl AccountAndDevice {
+ pub fn new(account_token: AccountToken, device: Device) -> Self {
+ Self {
+ account_token,
+ device,
}
}
}
@@ -122,22 +85,19 @@ impl From<DeviceData> for DeviceConfig {
#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))]
pub struct DeviceEvent {
/// Device that was affected.
- pub device: Option<DeviceConfig>,
+ pub device: Option<AccountAndDevice>,
/// Indicates whether the change was initiated remotely or by the daemon.
pub remote: bool,
}
impl DeviceEvent {
- pub fn new(data: Option<DeviceData>, remote: bool) -> DeviceEvent {
- DeviceEvent {
- device: data.map(DeviceConfig::from),
- remote,
- }
+ pub fn new(device: Option<AccountAndDevice>, remote: bool) -> DeviceEvent {
+ DeviceEvent { device, remote }
}
- pub fn from_device(data: DeviceData, remote: bool) -> DeviceEvent {
+ pub fn from_device(device: AccountAndDevice, remote: bool) -> DeviceEvent {
DeviceEvent {
- device: Some(DeviceConfig::from(data)),
+ device: Some(device),
remote,
}
}