aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock35
-rw-r--r--Cargo.toml1
-rw-r--r--README.md4
-rw-r--r--src/api.rs7
-rw-r--r--src/bin/rbw-agent/actions.rs31
-rw-r--r--src/bin/rbw/commands.rs32
-rw-r--r--src/edit.rs10
-rw-r--r--src/error.rs5
8 files changed, 90 insertions, 35 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7d4d24b..18f96b0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -133,6 +133,17 @@ dependencies = [
]
[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi 0.1.19",
+ "libc",
+ "winapi",
+]
+
+[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -683,6 +694,15 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hermit-abi"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
@@ -828,7 +848,7 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.3.2",
"libc",
"windows-sys 0.48.0",
]
@@ -845,7 +865,7 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.3.2",
"io-lifetimes",
"rustix",
"windows-sys 0.48.0",
@@ -1075,7 +1095,7 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.3.2",
"libc",
]
@@ -1296,6 +1316,7 @@ dependencies = [
"argon2",
"arrayvec",
"async-trait",
+ "atty",
"base32",
"base64",
"block-padding",
@@ -1564,9 +1585,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
-version = "0.101.1"
+version = "0.101.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15f36a6828982f422756984e47912a7a51dcbc2a197aa791158f8ca61cd8204e"
+checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d"
dependencies = [
"ring",
"untrusted",
@@ -2324,9 +2345,9 @@ dependencies = [
[[package]]
name = "webpki"
-version = "0.22.0"
+version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
+checksum = "f0e74f82d49d545ad128049b7e88f6576df2da6b02e9ce565c6f533be576957e"
dependencies = [
"ring",
"untrusted",
diff --git a/Cargo.toml b/Cargo.toml
index 2c5698b..a57be0d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,7 @@ anyhow = "1.0.72"
argon2 = "0.5.1"
arrayvec = "0.7.4"
async-trait = "0.1.71"
+atty="0.2.*"
base32 = "0.4.0"
base64 = "0.21.2"
block-padding = "0.3.3"
diff --git a/README.md b/README.md
index 423f31f..84c36f1 100644
--- a/README.md
+++ b/README.md
@@ -23,8 +23,8 @@ and merge pull requests implementing those features.
### Arch Linux
-`rbw` is available in the [community
-repository](https://archlinux.org/packages/community/x86_64/rbw/).
+`rbw` is available in the [extra
+repository](https://archlinux.org/packages/extra/x86_64/rbw/).
Alternatively, you can install
[`rbw-git`](https://aur.archlinux.org/packages/rbw-git/) from the AUR, which
will always build from the latest master commit.
diff --git a/src/api.rs b/src/api.rs
index bee0606..d21d997 100644
--- a/src/api.rs
+++ b/src/api.rs
@@ -1041,7 +1041,12 @@ impl Client {
history: &[crate::db::HistoryEntry],
) -> Result<()> {
let mut req = CiphersPutReq {
- ty: 1,
+ ty: match data {
+ crate::db::EntryData::Login {..} => 1,
+ crate::db::EntryData::SecureNote {..} => 2,
+ crate::db::EntryData::Card {..} => 3,
+ crate::db::EntryData::Identity {..} => 4,
+ },
folder_id: folder_uuid.map(std::string::ToString::to_string),
organization_id: org_id.map(std::string::ToString::to_string),
name: name.to_string(),
diff --git a/src/bin/rbw-agent/actions.rs b/src/bin/rbw-agent/actions.rs
index 58432b1..ff39510 100644
--- a/src/bin/rbw-agent/actions.rs
+++ b/src/bin/rbw-agent/actions.rs
@@ -10,8 +10,7 @@ pub async fn register(
let url_str = config_base_url().await?;
let url = reqwest::Url::parse(&url_str)
.context("failed to parse base url")?;
- let Some(host) = url.host_str()
- else {
+ let Some(host) = url.host_str() else {
return Err(anyhow::anyhow!(
"couldn't find host in rbw base url {}",
url_str
@@ -88,8 +87,7 @@ pub async fn login(
let url_str = config_base_url().await?;
let url = reqwest::Url::parse(&url_str)
.context("failed to parse base url")?;
- let Some(host) = url.host_str()
- else {
+ let Some(host) = url.host_str() else {
return Err(anyhow::anyhow!(
"couldn't find host in rbw base url {}",
url_str
@@ -330,8 +328,7 @@ async fn login_success(
sync(None, state.clone()).await?;
let db = load_db().await?;
- let Some(protected_private_key) = db.protected_private_key
- else {
+ let Some(protected_private_key) = db.protected_private_key else {
return Err(anyhow::anyhow!(
"failed to find protected private key in db"
));
@@ -369,15 +366,11 @@ pub async fn unlock(
if state.lock().await.needs_unlock() {
let db = load_db().await?;
- let Some(kdf) = db.kdf
- else {
- return Err(anyhow::anyhow!(
- "failed to find kdf type in db"
- ));
+ let Some(kdf) = db.kdf else {
+ return Err(anyhow::anyhow!("failed to find kdf type in db"));
};
- let Some(iterations) = db.iterations
- else {
+ let Some(iterations) = db.iterations else {
return Err(anyhow::anyhow!(
"failed to find number of iterations in db"
));
@@ -386,14 +379,12 @@ pub async fn unlock(
let memory = db.memory;
let parallelism = db.parallelism;
- let Some(protected_key) = db.protected_key
- else {
+ let Some(protected_key) = db.protected_key else {
return Err(anyhow::anyhow!(
"failed to find protected key in db"
));
};
- let Some(protected_private_key) = db.protected_private_key
- else {
+ let Some(protected_private_key) = db.protected_private_key else {
return Err(anyhow::anyhow!(
"failed to find protected private key in db"
));
@@ -543,8 +534,7 @@ pub async fn decrypt(
org_id: Option<&str>,
) -> anyhow::Result<()> {
let state = state.lock().await;
- let Some(keys) = state.key(org_id)
- else {
+ let Some(keys) = state.key(org_id) else {
return Err(anyhow::anyhow!(
"failed to find decryption keys in in-memory state"
));
@@ -570,8 +560,7 @@ pub async fn encrypt(
org_id: Option<&str>,
) -> anyhow::Result<()> {
let state = state.lock().await;
- let Some(keys) = state.key(org_id)
- else {
+ let Some(keys) = state.key(org_id) else {
return Err(anyhow::anyhow!(
"failed to find encryption keys in in-memory state"
));
diff --git a/src/bin/rbw/commands.rs b/src/bin/rbw/commands.rs
index 4ea3529..aa7f5b9 100644
--- a/src/bin/rbw/commands.rs
+++ b/src/bin/rbw/commands.rs
@@ -671,12 +671,17 @@ impl std::convert::TryFrom<&String> for ListField {
}
}
-const HELP: &str = r#"
+const HELP_PW: &str = r#"
# The first line of this file will be the password, and the remainder of the
# file (after any blank lines after the password) will be stored as a note.
# Lines with leading # will be ignored.
"#;
+const HELP_NOTES: &str = r#"
+# The content of this file will be stored as a note.
+# Lines with leading # will be ignored.
+"#;
+
pub fn config_show() -> anyhow::Result<()> {
let config = rbw::config::Config::load()?;
serde_json::to_writer_pretty(std::io::stdout(), &config)
@@ -939,7 +944,7 @@ pub fn add(
.map(|username| crate::actions::encrypt(username, None))
.transpose()?;
- let contents = rbw::edit::edit("", HELP)?;
+ let contents = rbw::edit::edit("", HELP_PW)?;
let (password, notes) = parse_editor(&contents);
let password = password
@@ -1140,7 +1145,7 @@ pub fn edit(
contents.push_str(&format!("\n{notes}\n"));
}
- let contents = rbw::edit::edit(&contents, HELP)?;
+ let contents = rbw::edit::edit(&contents, HELP_NOTES)?;
let (password, notes) = parse_editor(&contents);
let password = password
@@ -1188,6 +1193,27 @@ pub fn edit(
};
(data, notes, history)
}
+ DecryptedData::SecureNote {} =>
+ {
+ let data = rbw::db::EntryData::SecureNote {};
+
+ let editor_content = match decrypted.notes {
+ Some(notes) => format!("{notes}\n"),
+ None => format!("\n"),
+ };
+ let contents = rbw::edit::edit(&editor_content, HELP_NOTES)?;
+
+ // prepend blank line to be parsed as pw by `parse_editor`
+ let (_, notes) = parse_editor(&format!("\n{contents}\n"));
+
+ let notes = notes
+ .map(|notes| {
+ crate::actions::encrypt(&notes, entry.org_id.as_deref())
+ })
+ .transpose()?;
+
+ (data, notes, entry.history)
+ }
_ => {
return Err(anyhow::anyhow!(
"modifications are only supported for login entries"
diff --git a/src/edit.rs b/src/edit.rs
index aa8c7b1..cb59366 100644
--- a/src/edit.rs
+++ b/src/edit.rs
@@ -3,6 +3,14 @@ use crate::prelude::*;
use std::io::{Read as _, Write as _};
pub fn edit(contents: &str, help: &str) -> Result<String> {
+ if ! atty::is(atty::Stream::Stdin) {
+ // directly read from piped content
+ return match std::io::read_to_string(std::io::stdin()) {
+ Err(e) => Err(Error::FailedToReadFromStdin{ err: e }),
+ Ok(res) => Ok(res),
+ };
+ }
+
let mut var = "VISUAL";
let editor = std::env::var_os(var).unwrap_or_else(|| {
var = "EDITOR";
@@ -30,7 +38,7 @@ pub fn edit(contents: &str, help: &str) -> Result<String> {
let editor = std::path::Path::new(&editor);
let mut editor_args = vec![];
- #[allow(clippy::single_match)] // more to come
+ #[allow(clippy::single_match_else)] // more to come
match editor.file_name() {
Some(editor) => match editor.to_str() {
Some("vim" | "nvim") => {
diff --git a/src/error.rs b/src/error.rs
index 31edaf3..1ffbcdd 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -21,6 +21,11 @@ pub enum Error {
#[error("failed to parse pinentry output ({out:?})")]
FailedToParsePinentry { out: String },
+ #[error("failed to read from stdin: {err}")]
+ FailedToReadFromStdin {
+ err: std::io::Error,
+ },
+
#[error(
"failed to run editor {}: {err}",
.editor.to_string_lossy(),