diff options
| author | Emīls Piņķis <emils@mullvad.net> | 2018-08-27 14:48:07 +0100 |
|---|---|---|
| committer | Emīls Piņķis <emils@mullvad.net> | 2018-08-29 16:28:17 +0100 |
| commit | 7e329e2019ce87d32b8e4324400e6f3915739837 (patch) | |
| tree | 536481c0c5d1f682dce9fb1e975836c0e4d0a156 | |
| parent | cff2be6a76823fa250df6b3a69a796093634dbc6 (diff) | |
| download | mullvadvpn-7e329e2019ce87d32b8e4324400e6f3915739837.tar.xz mullvadvpn-7e329e2019ce87d32b8e4324400e6f3915739837.zip | |
Fix mullvad-tests
| -rw-r--r-- | mullvad-tests/Cargo.toml | 4 | ||||
| -rw-r--r-- | mullvad-tests/src/lib.rs | 90 | ||||
| -rw-r--r-- | mullvad-tests/tests/connection.rs | 112 | ||||
| -rw-r--r-- | mullvad-tests/tests/startup.rs | 55 |
4 files changed, 58 insertions, 203 deletions
diff --git a/mullvad-tests/Cargo.toml b/mullvad-tests/Cargo.toml index 7e7169c3f4..18f8b959d8 100644 --- a/mullvad-tests/Cargo.toml +++ b/mullvad-tests/Cargo.toml @@ -17,6 +17,10 @@ notify = "4.0" openvpn-plugin = { version = "0.3", features = ["serde"] } talpid-ipc = { path = "../talpid-ipc" } tempfile = "3.0" +jsonrpc-client-core = { git = "https://github.com/mullvad/jsonrpc-client-rs", branch = "master" } +jsonrpc-client-ipc = { git = "https://github.com/mullvad/jsonrpc-client-rs", branch = "master" } +tokio-core = "0.1" +futures = "0.1.23" [target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/mullvad-tests/src/lib.rs b/mullvad-tests/src/lib.rs index 0e2f404258..b90aaa3157 100644 --- a/mullvad-tests/src/lib.rs +++ b/mullvad-tests/src/lib.rs @@ -1,5 +1,7 @@ #[macro_use] extern crate duct; +extern crate jsonrpc_client_core; +extern crate jsonrpc_client_ipc; #[cfg(unix)] extern crate libc; extern crate mullvad_ipc_client; @@ -9,6 +11,9 @@ extern crate openvpn_plugin; extern crate talpid_ipc; extern crate tempfile; +extern crate futures; +extern crate tokio_core; + pub mod mock_openvpn; use std::collections::HashMap; @@ -18,12 +23,15 @@ use std::sync::{mpsc, Arc}; use std::time::{Duration, Instant}; use std::{cmp, thread}; -use mullvad_ipc_client::DaemonRpcClient; +use futures::sync::oneshot; +use jsonrpc_client_core::{Future, Transport}; +use jsonrpc_client_ipc::IpcTransport; +use mullvad_ipc_client::{DaemonRpcClient, ResultExt}; use mullvad_paths::resources::API_CA_FILENAME; use notify::{RawEvent, RecommendedWatcher, RecursiveMode, Watcher}; use openvpn_plugin::types::OpenVpnPluginEvent; -use talpid_ipc::WsIpcClient; use tempfile::TempDir; +use tokio_core::reactor::Core; use self::mock_openvpn::MOCK_OPENVPN_ARGS_FILE; use self::platform_specific::*; @@ -261,50 +269,42 @@ fn prepare_relay_list<T: AsRef<Path>>(path: T) { pub struct DaemonRunner { process: Option<duct::Handle>, mock_openvpn_args_file: PathBuf, - rpc_address_file: PathBuf, + rpc_socket_path: PathBuf, _temp_dir: TempDir, } impl DaemonRunner { pub fn spawn_with_real_rpc_address_file() -> Self { - Self::spawn_internal(false) + Self::spawn_internal() } pub fn spawn() -> Self { - Self::spawn_internal(true) + Self::spawn_internal() } - fn spawn_internal(mock_rpc_address_file: bool) -> Self { + fn spawn_internal() -> Self { let (temp_dir, cache_dir, resource_dir, settings_dir) = prepare_test_dirs(); let mock_openvpn_args_file = temp_dir.path().join(MOCK_OPENVPN_ARGS_FILE); - let rpc_address_file = if mock_rpc_address_file { - temp_dir.path().join(".mullvad_rpc_address") - } else { - mullvad_paths::get_rpc_address_path().expect("Failed to build RPC connection file path") - }; - let mut expression = cmd!(DAEMON_EXECUTABLE_PATH, "-v", "--disable-log-to-file") + let rpc_socket_path = temp_dir.path().join("rpc_socket"); + + let expression = cmd!(DAEMON_EXECUTABLE_PATH, "-v", "--disable-log-to-file") .dir("..") .env("MULLVAD_CACHE_DIR", cache_dir) + .env("MULLVAD_RPC_SOCKET_PATH", rpc_socket_path.clone()) .env("MULLVAD_RESOURCE_DIR", resource_dir) .env("MULLVAD_SETTINGS_DIR", settings_dir) .env("MOCK_OPENVPN_ARGS_FILE", mock_openvpn_args_file.clone()) .stdout_null() .stderr_null(); - if mock_rpc_address_file { - expression = expression.env( - "MULLVAD_RPC_ADDRESS_PATH", - rpc_address_file.display().to_string(), - ); - } let process = expression.start().expect("Failed to start daemon"); DaemonRunner { process: Some(process), mock_openvpn_args_file, - rpc_address_file, + rpc_socket_path, _temp_dir: temp_dir, } } @@ -314,10 +314,12 @@ impl DaemonRunner { } pub fn rpc_client(&mut self) -> Result<DaemonRpcClient> { - wait_for_file(&self.rpc_address_file); - - DaemonRpcClient::with_insecure_rpc_address_file(&self.rpc_address_file) - .map_err(|error| format!("Failed to create RPC client: {}", error)) + wait_for_file(&self.rpc_socket_path); + let socket_path: String = self.rpc_socket_path.to_string_lossy().to_string(); + mullvad_ipc_client::new_standalone_transport(socket_path, |path, handle| { + IpcTransport::new(&path, &handle) + .chain_err(|| mullvad_ipc_client::ErrorKind::TransportError) + }).map_err(|e| format!("Failed to construct an RPC client - {}", e)) } #[cfg(unix)] @@ -360,35 +362,38 @@ impl Drop for DaemonRunner { process.kill().unwrap(); } } - - let _ = fs::remove_file(&self.rpc_address_file); } } pub struct MockOpenVpnPluginRpcClient { - credentials: String, - rpc: WsIpcClient, + rpc: jsonrpc_client_core::ClientHandle, } impl MockOpenVpnPluginRpcClient { - pub fn new(address: String, credentials: String) -> Result<Self> { - let rpc = WsIpcClient::connect(&address).map_err(|error| { - format!("Failed to create Mock OpenVPN plugin RPC client: {}", error) - })?; + fn spawn_event_loop(address: String) -> Result<jsonrpc_client_core::ClientHandle> { + let (tx, rx) = oneshot::channel(); + thread::spawn(move || { + let mut core = Core::new().expect("failed to spawn an event loop"); - Ok(MockOpenVpnPluginRpcClient { rpc, credentials }) - } + let result = IpcTransport::new(&address, &core.handle()) + .map_err(|error| { + format!("Failed to create Mock OpenVPN plugin RPC client: {}", error) + }).map(Transport::into_client); + match result { + Ok((client, client_handle)) => { + tx.send(Ok(client_handle)).unwrap(); + core.run(client).expect("client failed"); + } + Err(e) => tx.send(Err(e)).unwrap(), + } + }); - pub fn authenticate(&mut self) -> Result<bool> { - self.rpc - .call("authenticate", &[&self.credentials]) - .map_err(|error| format!("Failed to authenticate mock OpenVPN IPC client: {}", error)) + rx.wait().unwrap() } - pub fn authenticate_with(&mut self, credentials: &str) -> Result<bool> { - self.rpc - .call("authenticate", &[credentials]) - .map_err(|error| format!("Failed to authenticate mock OpenVPN IPC client: {}", error)) + pub fn new(address: String) -> Result<Self> { + let rpc = Self::spawn_event_loop(address)?; + Ok(MockOpenVpnPluginRpcClient { rpc }) } pub fn up(&mut self) -> Result<()> { @@ -417,7 +422,8 @@ impl MockOpenVpnPluginRpcClient { env: HashMap<String, String>, ) -> Result<()> { self.rpc - .call("openvpn_event", &(event, env)) + .call_method("openvpn_event", &(event, env)) + .wait() .map_err(|error| format!("Failed to send mock OpenVPN event {:?}: {}", event, error)) } } diff --git a/mullvad-tests/tests/connection.rs b/mullvad-tests/tests/connection.rs index 88e8a88c74..75ee04d788 100644 --- a/mullvad-tests/tests/connection.rs +++ b/mullvad-tests/tests/connection.rs @@ -34,11 +34,6 @@ const CONNECTED_STATE: DaemonState = DaemonState { target_state: TargetState::Secured, }; -const DISCONNECTING_STATE: DaemonState = DaemonState { - state: SecurityState::Secured, - target_state: TargetState::Unsecured, -}; - #[test] fn spawns_openvpn() { let mut daemon = DaemonRunner::spawn(); @@ -91,86 +86,6 @@ fn changes_to_connecting_state() { } #[test] -fn ignores_event_from_unauthorized_connection_from_openvpn_plugin() { - let mut daemon = DaemonRunner::spawn(); - let mut rpc_client = daemon.rpc_client().unwrap(); - let openvpn_args_file = daemon.mock_openvpn_args_file(); - let mut openvpn_args_file_events = PathWatcher::watch(&openvpn_args_file).unwrap(); - let state_events = rpc_client.new_state_subscribe().unwrap(); - - rpc_client.set_account(Some("123456".to_owned())).unwrap(); - rpc_client.connect().unwrap(); - - assert_state_event(&state_events, CONNECTING_STATE); - openvpn_args_file_events.assert_create_write_close_sequence(); - - let mut mock_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file); - let call_result = mock_plugin_client.up(); - - assert!(call_result.is_err()); - assert_no_state_event(&state_events); - assert_eq!(rpc_client.get_state().unwrap(), CONNECTING_STATE); -} - -#[test] -fn authentication_credentials() { - let mut daemon = DaemonRunner::spawn(); - let mut rpc_client = daemon.rpc_client().unwrap(); - let openvpn_args_file = daemon.mock_openvpn_args_file(); - let mut openvpn_args_file_events = PathWatcher::watch(&openvpn_args_file).unwrap(); - let state_events = rpc_client.new_state_subscribe().unwrap(); - - rpc_client.set_account(Some("123456".to_owned())).unwrap(); - rpc_client.connect().unwrap(); - - assert_state_event(&state_events, CONNECTING_STATE); - openvpn_args_file_events.assert_create_write_close_sequence(); - - let mut mock_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file); - - assert_eq!( - mock_plugin_client.authenticate_with(&String::new()), - Ok(false) - ); - assert_eq!( - mock_plugin_client.authenticate_with(&"fake-secret".to_owned()), - Ok(false) - ); - assert_eq!(mock_plugin_client.authenticate(), Ok(true)); - // Ensure it doesn't accept additional incorrect credentials - assert_eq!( - mock_plugin_client.authenticate_with(&"different-secret".to_owned()), - Ok(false) - ); -} - -#[test] -fn separate_connections_have_independent_authentication() { - let mut daemon = DaemonRunner::spawn(); - let mut rpc_client = daemon.rpc_client().unwrap(); - let openvpn_args_file = daemon.mock_openvpn_args_file(); - let mut openvpn_args_file_events = PathWatcher::watch(&openvpn_args_file).unwrap(); - let state_events = rpc_client.new_state_subscribe().unwrap(); - - rpc_client.set_account(Some("123456".to_owned())).unwrap(); - rpc_client.connect().unwrap(); - - assert_state_event(&state_events, CONNECTING_STATE); - openvpn_args_file_events.assert_create_write_close_sequence(); - - let mut first_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file); - let mut second_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file); - - let auth_result = first_plugin_client.authenticate(); - let call_result = second_plugin_client.up(); - - assert_eq!(auth_result, Ok(true)); - assert!(call_result.is_err()); - assert_no_state_event(&state_events); - assert_eq!(rpc_client.get_state().unwrap(), CONNECTING_STATE); -} - -#[test] fn changes_to_connected_state() { let mut daemon = DaemonRunner::spawn(); let mut rpc_client = daemon.rpc_client().unwrap(); @@ -186,7 +101,6 @@ fn changes_to_connected_state() { let mut mock_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file); - mock_plugin_client.authenticate().unwrap(); mock_plugin_client.up().unwrap(); assert_state_event(&state_events, CONNECTED_STATE); @@ -209,7 +123,6 @@ fn returns_to_connecting_state() { let mut mock_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file); - mock_plugin_client.authenticate().unwrap(); mock_plugin_client.up().unwrap(); assert_state_event(&state_events, CONNECTED_STATE); @@ -240,14 +153,12 @@ fn disconnects() { let mut mock_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file); - mock_plugin_client.authenticate().unwrap(); mock_plugin_client.up().unwrap(); assert_state_event(&state_events, CONNECTED_STATE); rpc_client.disconnect().unwrap(); - assert_state_event(&state_events, DISCONNECTING_STATE); assert_state_event(&state_events, DISCONNECTED_STATE); assert_eq!(rpc_client.get_state().unwrap(), DISCONNECTED_STATE); } @@ -260,33 +171,20 @@ fn assert_state_event(receiver: &mpsc::Receiver<DaemonState>, expected_state: Da assert_eq!(received_state, expected_state); } -fn assert_no_state_event(receiver: &mpsc::Receiver<DaemonState>) { - assert_eq!( - receiver.recv_timeout(Duration::from_secs(1)), - Err(mpsc::RecvTimeoutError::Timeout), - ); -} - fn create_mock_openvpn_plugin_client<P: AsRef<Path>>( openvpn_args_file_path: P, ) -> MockOpenVpnPluginRpcClient { - let (address, credentials) = get_plugin_arguments(openvpn_args_file_path); + let address = get_plugin_arguments(openvpn_args_file_path); - MockOpenVpnPluginRpcClient::new(address, credentials) + MockOpenVpnPluginRpcClient::new(address) .expect("Failed to create mock RPC client to connect to OpenVPN plugin event listener") } -fn get_plugin_arguments<P: AsRef<Path>>(openvpn_args_file_path: P) -> (String, String) { +fn get_plugin_arguments<P: AsRef<Path>>(openvpn_args_file_path: P) -> String { let mut arguments = search_openvpn_args(openvpn_args_file_path, OPENVPN_PLUGIN_NAME).skip(1); - let address = arguments + arguments .next() .expect("Missing OpenVPN plugin RPC listener address argument") - .expect("Failed to read from mock OpenVPN arguments file"); - let credentials = arguments - .next() - .expect("Missing OpenVPN plugin RPC listener credentials argument") - .expect("Failed to read from mock OpenVPN arguments file"); - - (address, credentials) + .expect("Failed to read from mock OpenVPN arguments file") } diff --git a/mullvad-tests/tests/startup.rs b/mullvad-tests/tests/startup.rs index 3bf47430cc..0424023515 100644 --- a/mullvad-tests/tests/startup.rs +++ b/mullvad-tests/tests/startup.rs @@ -4,39 +4,9 @@ extern crate mullvad_paths; extern crate mullvad_tests; extern crate mullvad_types; -use std::fs::{self, Metadata}; -use std::io; -use std::time::Duration; - -use mullvad_tests::{DaemonRunner, PathWatcher}; +use mullvad_tests::DaemonRunner; use mullvad_types::states::{DaemonState, SecurityState, TargetState}; -use platform_specific::*; - -#[test] -fn rpc_info_file_permissions() { - let rpc_file = mullvad_paths::get_rpc_address_path().unwrap(); - - if let Err(error) = fs::remove_file(&rpc_file) { - if error.kind() != io::ErrorKind::NotFound { - panic!("failed to remove existing RPC address file"); - } - } - - assert!(!rpc_file.exists()); - - let mut rpc_file_watcher = PathWatcher::watch(&rpc_file).unwrap(); - let _daemon = DaemonRunner::spawn_with_real_rpc_address_file(); - - rpc_file_watcher.wait_for_burst_of_events(Duration::from_secs(10)); - - assert!(rpc_file.exists()); - - ensure_only_admin_can_write( - fs::metadata(&rpc_file).expect("Failed to read RPC address file metadata"), - ); -} - #[test] fn starts_in_not_connected_state() { let mut daemon = DaemonRunner::spawn(); @@ -50,26 +20,3 @@ fn starts_in_not_connected_state() { assert_eq!(state, not_connected_state); } - -#[cfg(unix)] -mod platform_specific { - extern crate libc; - - use super::*; - use std::os::unix::fs::MetadataExt; - - pub fn ensure_only_admin_can_write(metadata: Metadata) { - let process_uid = unsafe { libc::getuid() }; - assert_eq!(metadata.uid(), process_uid); - assert_eq!(metadata.mode() & 0o022, 0); - } -} - -#[cfg(not(unix))] -mod platform_specific { - use super::*; - - pub fn ensure_only_admin_can_write(_metadata: Metadata) { - // TODO: Test when correctly implemented on Windows - } -} |
