Remove openbao helper and replace it with more general program
This gets rid of the messy nix code for handling bitwarden secrets, and unifies it all into a nice single program in rust. Ensuring that only the needed secrets are loaded.
This commit is contained in:
parent
e6a152e95c
commit
8640dce7bc
31 changed files with 1159 additions and 958 deletions
|
@ -108,7 +108,7 @@ pub enum BitwardenEntryFieldType {
|
|||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||
pub struct BitwardenEntryField {
|
||||
pub name: String,
|
||||
pub name: Option<String>,
|
||||
pub value: Option<String>,
|
||||
#[serde(rename = "type")]
|
||||
pub field_type: BitwardenEntryFieldType,
|
||||
|
@ -154,6 +154,7 @@ pub enum BitwardenOrganizationUserStatus {
|
|||
|
||||
pub struct BitwardenSession {
|
||||
session_id: Option<String>,
|
||||
items: Option<Vec<BitwardenEntry>>,
|
||||
}
|
||||
|
||||
impl BitwardenSession {
|
||||
|
@ -171,11 +172,8 @@ impl BitwardenSession {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn new_or_authenticate(
|
||||
username: Option<&str>,
|
||||
bw_unlock_purpose: &str,
|
||||
) -> anyhow::Result<Self> {
|
||||
let bw = BitwardenSession::new_if_authenticated(username, bw_unlock_purpose, true)?;
|
||||
pub fn unlock() -> anyhow::Result<Self> {
|
||||
let bw = BitwardenSession::new_if_authenticated(true)?;
|
||||
if let Some(bw) = bw {
|
||||
bw.sync()?;
|
||||
Ok(bw)
|
||||
|
@ -187,7 +185,7 @@ impl BitwardenSession {
|
|||
.stderr_inherit()
|
||||
.try_spawn_to_string()?;
|
||||
// Just logged in, no point in syncing
|
||||
let bw = BitwardenSession::new_if_authenticated(username, bw_unlock_purpose, false)?;
|
||||
let bw = BitwardenSession::new_if_authenticated(false)?;
|
||||
if let Some(bw) = bw {
|
||||
Ok(bw)
|
||||
} else {
|
||||
|
@ -198,67 +196,61 @@ impl BitwardenSession {
|
|||
}
|
||||
}
|
||||
|
||||
fn new_if_authenticated(
|
||||
username: Option<&str>,
|
||||
bw_unlock_purpose: &str,
|
||||
sync: bool,
|
||||
) -> anyhow::Result<Option<Self>> {
|
||||
fn new_if_authenticated(sync: bool) -> anyhow::Result<Option<Self>> {
|
||||
let status: BitwardenAuthenticationStatus = proc::Command::new("bw")
|
||||
.args(["--nointeraction", "status"])
|
||||
.try_spawn_to_json()?;
|
||||
|
||||
let Some(user) = status.user() else {
|
||||
let Some(_) = status.user() else {
|
||||
return Ok(None);
|
||||
};
|
||||
if let Some(username) = username {
|
||||
if user.user_email != username {
|
||||
return Err(anyhow::format_err!(
|
||||
"Authenticated user in bitwarden does not match the expected user of {}, was {}",
|
||||
username,
|
||||
user.user_email
|
||||
));
|
||||
}
|
||||
}
|
||||
let is_unlocked: bool = matches!(status, BitwardenAuthenticationStatus::Unlocked(_));
|
||||
if sync && !is_unlocked {
|
||||
log::info!("Syncing Bitwarden...");
|
||||
let _ = proc::Command::new("bw").arg("sync").try_spawn_to_string()?;
|
||||
}
|
||||
log::info!("Unlocking bitwarden...");
|
||||
let session_id = if is_unlocked {
|
||||
None
|
||||
} else {
|
||||
log::info!("Unlocking bitwarden...");
|
||||
Some(
|
||||
proc::Command::new("bitwarden-unlock")
|
||||
.args(["--purpose", bw_unlock_purpose])
|
||||
proc::Command::new("bw")
|
||||
.args(["unlock", "--raw"])
|
||||
.stderr_inherit()
|
||||
.stdin_inherit()
|
||||
.try_spawn_to_string()?,
|
||||
)
|
||||
};
|
||||
Ok(Some(Self { session_id }))
|
||||
Ok(Some(Self {
|
||||
session_id,
|
||||
items: None,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn list_items(&self) -> anyhow::Result<Vec<BitwardenEntry>> {
|
||||
log::info!("Listing bitwarden items...");
|
||||
self.bw_command()
|
||||
// Pretty format for better error messages during json decoding issues
|
||||
.args(["--pretty", "list", "items"])
|
||||
.try_spawn_to_json()
|
||||
pub fn list_items(&mut self) -> anyhow::Result<&[BitwardenEntry]> {
|
||||
if self.items.is_none() {
|
||||
log::info!("Listing bitwarden items...");
|
||||
let result = self
|
||||
.bw_command()
|
||||
// Pretty format for better error messages during json decoding issues
|
||||
.args(["--pretty", "list", "items"])
|
||||
.try_spawn_to_json()?;
|
||||
|
||||
let _ = self.items.insert(result);
|
||||
}
|
||||
Ok(self.items.as_deref().expect("Items have just been set"))
|
||||
}
|
||||
|
||||
pub fn get_item(&self, name: &str) -> anyhow::Result<Option<BitwardenEntry>> {
|
||||
let mut items = self.list_items()?;
|
||||
let Some(idx) = items
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(idx, e)| if e.name() == name { Some(idx) } else { None })
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let item = items.swap_remove(idx);
|
||||
Ok(Some(item))
|
||||
pub fn get_item(&mut self, name: &str) -> anyhow::Result<Option<&BitwardenEntry>> {
|
||||
let items = self.list_items()?;
|
||||
Ok(items.iter().find(|e| e.name() == name))
|
||||
}
|
||||
|
||||
pub fn get_attachment(&self, entry: &BitwardenEntry, name: &str) -> anyhow::Result<Vec<u8>> {
|
||||
pub fn get_attachment(
|
||||
&mut self,
|
||||
entry: &BitwardenEntry,
|
||||
name: &str,
|
||||
) -> anyhow::Result<Vec<u8>> {
|
||||
self.bw_command()
|
||||
.args(["get", "attachment", name, "--itemid"])
|
||||
.arg(entry.id.as_str())
|
||||
|
@ -266,22 +258,19 @@ impl BitwardenSession {
|
|||
.try_spawn_to_bytes()
|
||||
}
|
||||
|
||||
pub fn list_own_items(&self) -> anyhow::Result<Vec<BitwardenEntry>> {
|
||||
let mut items = self.list_items()?;
|
||||
items.retain(|i| i.organization_id.as_ref().is_none_or(|o| o.is_empty()));
|
||||
Ok(items)
|
||||
}
|
||||
|
||||
pub fn create_item(&self, item: &CommandBitwardenEntry) -> anyhow::Result<BitwardenEntry> {
|
||||
pub fn create_item(&mut self, item: &CommandBitwardenEntry) -> anyhow::Result<BitwardenEntry> {
|
||||
log::info!("Creating bitwarden entry {name}", name = item.name);
|
||||
self.bw_command()
|
||||
let res = self
|
||||
.bw_command()
|
||||
.args(["create", "item"])
|
||||
.stdin_json_base64(item)?
|
||||
.try_spawn_to_json()
|
||||
.try_spawn_to_json()?;
|
||||
let _ = self.items.take();
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn update_item(
|
||||
&self,
|
||||
&mut self,
|
||||
to_update: &BitwardenEntry,
|
||||
update_with: &CommandBitwardenEntry,
|
||||
) -> anyhow::Result<()> {
|
||||
|
@ -295,10 +284,11 @@ impl BitwardenSession {
|
|||
.args(["edit", "item", &to_update.id])
|
||||
.stdin_json_base64(update_with)?
|
||||
.try_spawn_to_string()?;
|
||||
let _ = self.items.take();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_item(&self, to_delete: &BitwardenEntry) -> anyhow::Result<()> {
|
||||
pub fn delete_item(&mut self, to_delete: &BitwardenEntry) -> anyhow::Result<()> {
|
||||
log::info!(
|
||||
"Deleting bitwarden entry {name}, with id: {id}",
|
||||
id = to_delete.id,
|
||||
|
@ -308,13 +298,13 @@ impl BitwardenSession {
|
|||
.bw_command()
|
||||
.args(["delete", "item", &to_delete.id])
|
||||
.try_spawn_to_string()?;
|
||||
let _ = self.items.take();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BitwardenSession {
|
||||
fn drop(&mut self) {
|
||||
log::info!("Locking bitwarden session...");
|
||||
if self.session_id.is_some() {
|
||||
if let Err(e) = self
|
||||
.bw_command()
|
||||
|
@ -349,9 +339,6 @@ impl BitwardenAuthenticationStatus {
|
|||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct BitwardenAuthenticationUser {
|
||||
#[serde(rename = "userEmail")]
|
||||
user_email: String,
|
||||
|
||||
#[serde(rename = "userId")]
|
||||
#[allow(dead_code)]
|
||||
user_id: String,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue