Allow using existing key file on login (#688)

* 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.
This commit is contained in:
Trygve Aaberge 2023-02-10 20:35:38 +01:00 committed by GitHub
parent 78b54662cd
commit 2cec7ba677
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -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;
@ -44,32 +44,36 @@ 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 session = api_client::login(
settings.sync_address.as_str(),
LoginRequest { username, password },
)
.await?;
let session_path = settings.session_path.as_str();
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?;
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")?,
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::InvalidChecksum => {
bail!("key mnemonic was not valid")
}
bip39::ErrorKind::InvalidKeysize(_)
| bip39::ErrorKind::InvalidWordLength(_)
| bip39::ErrorKind::InvalidEntropyLength(_, _) => {
@ -83,7 +87,23 @@ impl Cmd {
}
};
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 },
)
.await?;
let session_path = settings.session_path.as_str();
let mut file = File::create(session_path).await?;
file.write_all(session.session.as_bytes()).await?;
println!("Logged in!");