Skip to content

Commit

Permalink
Allow using existing key file on login (#688)
Browse files Browse the repository at this point in the history
* Allow logging in without overwriting existing key file

If the given key on login in empty, keep the existing key file rather
than overwriting it with an empty file. This is useful if you log out
and want to log in again and still use the same key, or if you have
copied over the key file rather than providing it as input.

* Refuse logging in if key is empty

Before the previous commit, an empty key file would be created if key
wasn't specified, and after the previous commit, the key file would not
be created if the key wasn't specified and stay empty if it was empty.

Now the log command checks the key file if a key is not specified and
exits with an error message if either the key file couldn't be opened or
is empty. If a key is specified, the key file is just created with it as
before.

* Validate the key on login, create new if no exists

After reading the key either from an existing key file, or from the user
input, validate that the provided key is valid (rather than just
checking that it isn't empty). If no key file exists, create a new key
instead of erroring out.
  • Loading branch information
trygveaa committed Feb 10, 2023
1 parent 78b5466 commit 2cec7ba
Showing 1 changed file with 53 additions and 33 deletions.
86 changes: 53 additions & 33 deletions src/command/client/sync/login.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::io;
use std::{io, path::PathBuf};

use clap::Parser;
use eyre::{bail, ContextCompat, Result};
use eyre::{bail, Context, ContextCompat, Result};
use tokio::{fs::File, io::AsyncWriteExt};

use atuin_client::{
api_client,
encryption::{encode_key, Key},
encryption::{decode_key, encode_key, new_key, Key},
settings::Settings,
};
use atuin_common::api::LoginRequest;
Expand Down Expand Up @@ -44,8 +44,57 @@ impl Cmd {
}

let username = or_user_input(&self.username, "username");
let key = or_user_input(&self.key, "encryption key");
let key = or_user_input(&self.key, "encryption key [blank to use existing key file]");
let password = self.password.clone().unwrap_or_else(read_user_password);

let key_path = settings.key_path.as_str();
if key.is_empty() {
if PathBuf::from(key_path).exists() {
let bytes = fs_err::read_to_string(key_path)
.context("existing key file couldn't be read")?;
if decode_key(bytes).is_err() {
bail!("the key in existing key file was invalid");
}
} else {
println!("No key file exists, creating a new");
let _key = new_key(settings)?;
}
} else {
// try parse the key as a mnemonic...
let key = match bip39::Mnemonic::from_phrase(&key, bip39::Language::English) {
Ok(mnemonic) => encode_key(
Key::from_slice(mnemonic.entropy())
.context("key was not the correct length")?,
)?,
Err(err) => {
if let Some(err) = err.downcast_ref::<bip39::ErrorKind>() {
match err {
// assume they copied in the base64 key
bip39::ErrorKind::InvalidWord => key,
bip39::ErrorKind::InvalidChecksum => {
bail!("key mnemonic was not valid")
}
bip39::ErrorKind::InvalidKeysize(_)
| bip39::ErrorKind::InvalidWordLength(_)
| bip39::ErrorKind::InvalidEntropyLength(_, _) => {
bail!("key was not the correct length")
}
}
} else {
// unknown error. assume they copied the base64 key
key
}
}
};

if decode_key(key.clone()).is_err() {
bail!("the specified key was invalid");
}

let mut file = File::create(key_path).await?;
file.write_all(key.as_bytes()).await?;
}

let session = api_client::login(
settings.sync_address.as_str(),
LoginRequest { username, password },
Expand All @@ -56,35 +105,6 @@ impl Cmd {
let mut file = File::create(session_path).await?;
file.write_all(session.session.as_bytes()).await?;

let key_path = settings.key_path.as_str();
let mut file = File::create(key_path).await?;

// try parse the key as a mnemonic...
let key = match bip39::Mnemonic::from_phrase(&key, bip39::Language::English) {
Ok(mnemonic) => encode_key(
Key::from_slice(mnemonic.entropy()).context("key was not the correct length")?,
)?,
Err(err) => {
if let Some(err) = err.downcast_ref::<bip39::ErrorKind>() {
match err {
// assume they copied in the base64 key
bip39::ErrorKind::InvalidWord => key,
bip39::ErrorKind::InvalidChecksum => bail!("key mnemonic was not valid"),
bip39::ErrorKind::InvalidKeysize(_)
| bip39::ErrorKind::InvalidWordLength(_)
| bip39::ErrorKind::InvalidEntropyLength(_, _) => {
bail!("key was not the correct length")
}
}
} else {
// unknown error. assume they copied the base64 key
key
}
}
};

file.write_all(key.as_bytes()).await?;

println!("Logged in!");

Ok(())
Expand Down

0 comments on commit 2cec7ba

Please sign in to comment.