summaryrefslogtreecommitdiffhomepage
path: root/mullvad-cli/src/cmds
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2021-10-29 16:07:53 +0200
committerDavid Lönnhager <david.l@mullvad.net>2022-03-14 12:08:39 +0100
commite2644cab30eed66233b4e191ac1d30299bc8c171 (patch)
treed23c50a6b34966cfe5a5d7c95b06f3bb6cda775f /mullvad-cli/src/cmds
parent8baa2cab6965a3ed3597c34201ad540c5ed10ca8 (diff)
downloadmullvadvpn-e2644cab30eed66233b4e191ac1d30299bc8c171.tar.xz
mullvadvpn-e2644cab30eed66233b4e191ac1d30299bc8c171.zip
Add CLI commands for listing and revoking devices
Diffstat (limited to 'mullvad-cli/src/cmds')
-rw-r--r--mullvad-cli/src/cmds/account.rs100
1 files changed, 83 insertions, 17 deletions
diff --git a/mullvad-cli/src/cmds/account.rs b/mullvad-cli/src/cmds/account.rs
index fae3b39396..4b5a4f1fbd 100644
--- a/mullvad-cli/src/cmds/account.rs
+++ b/mullvad-cli/src/cmds/account.rs
@@ -1,6 +1,9 @@
use crate::{new_rpc_client, Command, Error, Result};
use itertools::Itertools;
-use mullvad_management_interface::{types::Timestamp, Code};
+use mullvad_management_interface::{
+ types::{self, Timestamp},
+ Code,
+};
use mullvad_types::account::AccountToken;
use std::io::{self, Write};
@@ -29,6 +32,29 @@ impl Command for Account {
clap::App::new("get").about("Display information about the current account"),
)
.subcommand(
+ clap::App::new("list-devices")
+ .about("List devices associated with an account")
+ .arg(
+ clap::Arg::new("token")
+ .help("Mullvad account number")
+ .required(true),
+ ),
+ )
+ .subcommand(
+ clap::App::new("revoke-device")
+ .about("Revoke a device associated with an account")
+ .arg(
+ clap::Arg::new("token")
+ .help("Mullvad account number")
+ .required(true),
+ )
+ .arg(
+ clap::Arg::new("device")
+ .help("ID of the device to revoke")
+ .required(true),
+ ),
+ )
+ .subcommand(
clap::App::new("redeem").about("Redeems a voucher").arg(
clap::Arg::new("voucher")
.help("The Mullvad voucher code to be submitted")
@@ -41,26 +67,16 @@ impl Command for Account {
if let Some(_matches) = matches.subcommand_matches("create") {
self.create().await
} else if let Some(set_matches) = matches.subcommand_matches("login") {
- let mut token = match set_matches.value_of("token") {
- Some(token) => token.to_string(),
- None => {
- let mut token = String::new();
- io::stdout()
- .write_all(b"Enter account token: ")
- .expect("Failed to write to STDOUT");
- let _ = io::stdout().flush();
- io::stdin()
- .read_line(&mut token)
- .expect("Failed to read from STDIN");
- token
- }
- };
- token = token.split_whitespace().join("").to_string();
- self.login(token).await
+ self.login(parse_token(set_matches)).await
} else if let Some(_matches) = matches.subcommand_matches("logout") {
self.logout().await
} else if let Some(_matches) = matches.subcommand_matches("get") {
self.get().await
+ } else if let Some(set_matches) = matches.subcommand_matches("list-devices") {
+ self.list_devices(parse_token(set_matches)).await
+ } else if let Some(set_matches) = matches.subcommand_matches("revoke-device") {
+ self.revoke_device(parse_token(set_matches), parse_device_id(set_matches))
+ .await
} else if let Some(matches) = matches.subcommand_matches("redeem") {
let voucher = matches.value_of_t_or_exit("voucher");
self.redeem_voucher(voucher).await
@@ -113,6 +129,26 @@ impl Account {
Ok(())
}
+ async fn list_devices(&self, token: String) -> Result<()> {
+ let mut rpc = new_rpc_client().await?;
+ let devices = rpc.list_devices(token).await?.into_inner();
+
+ println!("{:?}", devices);
+
+ Ok(())
+ }
+
+ async fn revoke_device(&self, token: String, device_id: String) -> Result<()> {
+ let mut rpc = new_rpc_client().await?;
+ rpc.remove_device(types::DeviceRemoval {
+ account_token: token,
+ device_id,
+ })
+ .await?;
+ println!("Removed device");
+ Ok(())
+ }
+
async fn redeem_voucher(&self, mut voucher: String) -> Result<()> {
let mut rpc = new_rpc_client().await?;
voucher.retain(|c| c.is_alphanumeric());
@@ -161,3 +197,33 @@ impl Account {
utc.with_timezone(&chrono::Local).to_string()
}
}
+
+fn parse_token(matches: &clap::ArgMatches) -> String {
+ parse_from_match_else_stdin("Enter account token: ", "token", matches)
+}
+
+fn parse_device_id(matches: &clap::ArgMatches) -> String {
+ parse_from_match_else_stdin("Enter device id: ", "device", matches)
+}
+
+fn parse_from_match_else_stdin(
+ prompt_str: &'static str,
+ key: &'static str,
+ matches: &clap::ArgMatches,
+) -> String {
+ let val = match matches.value_of(key) {
+ Some(device) => device.to_string(),
+ None => {
+ let mut val = String::new();
+ io::stdout()
+ .write_all(prompt_str.as_bytes())
+ .expect("Failed to write to STDOUT");
+ let _ = io::stdout().flush();
+ io::stdin()
+ .read_line(&mut val)
+ .expect("Failed to read from STDIN");
+ val
+ }
+ };
+ val.split_whitespace().join("").to_string()
+}