use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use futures::channel::mpsc;
use log::debug;
use zbus::Connection;
use crate::ConnectionError;
use crate::dbus::AgentManagerProxy;
use super::iface::SecretAgentInterface;
use super::request::{CancelReason, SecretAgentCapabilities, SecretRequest, SecretStoreEvent};
const DEFAULT_IDENTIFIER: &str = "com.system76.CosmicApplets.nmrs.secret_agent";
const DEFAULT_OBJECT_PATH: &str = "/com/system76/CosmicApplets/nmrs/SecretAgent";
const DEFAULT_QUEUE_DEPTH: usize = 32;
pub struct SecretAgent;
impl SecretAgent {
#[must_use]
pub fn builder() -> SecretAgentBuilder {
SecretAgentBuilder::default()
}
}
#[derive(Debug)]
pub struct SecretAgentBuilder {
identifier: String,
capabilities: SecretAgentCapabilities,
object_path: String,
queue_depth: usize,
}
impl Default for SecretAgentBuilder {
fn default() -> Self {
Self {
identifier: DEFAULT_IDENTIFIER.into(),
capabilities: SecretAgentCapabilities::VPN_HINTS,
object_path: DEFAULT_OBJECT_PATH.into(),
queue_depth: DEFAULT_QUEUE_DEPTH,
}
}
}
impl SecretAgentBuilder {
#[must_use]
pub fn with_identifier(mut self, identifier: impl Into<String>) -> Self {
self.identifier = identifier.into();
self
}
#[must_use]
pub fn with_capabilities(mut self, capabilities: SecretAgentCapabilities) -> Self {
self.capabilities = capabilities;
self
}
#[must_use]
pub fn with_object_path(mut self, path: impl Into<String>) -> Self {
self.object_path = path.into();
self
}
#[must_use]
pub fn with_queue_depth(mut self, depth: usize) -> Self {
self.queue_depth = depth;
self
}
pub async fn register(
self,
) -> crate::Result<(SecretAgentHandle, mpsc::Receiver<SecretRequest>)> {
let (request_tx, request_rx) = mpsc::channel(self.queue_depth);
let (cancel_tx, cancel_rx) = mpsc::unbounded();
let (store_tx, store_rx) = mpsc::unbounded();
let iface = SecretAgentInterface {
request_tx,
cancel_tx,
store_tx,
pending: Arc::new(Mutex::new(HashMap::new())),
};
let conn = Connection::system()
.await
.map_err(|e| ConnectionError::DbusOperation {
context: "connecting to system bus for secret agent".into(),
source: e,
})?;
conn.object_server()
.at(&*self.object_path, iface)
.await
.map_err(|e| ConnectionError::DbusOperation {
context: format!("serving SecretAgent interface at {}", self.object_path),
source: e,
})?;
conn.request_name(&*self.identifier).await.map_err(|e| {
ConnectionError::AgentRegistration {
context: format!("bus name '{}': {e}", self.identifier),
}
})?;
debug!(
"Acquired bus name '{}', serving at '{}'",
self.identifier, self.object_path
);
let agent_proxy =
AgentManagerProxy::new(&conn)
.await
.map_err(|e| ConnectionError::DbusOperation {
context: "creating AgentManager proxy".into(),
source: e,
})?;
agent_proxy
.register_with_capabilities(&self.identifier, self.capabilities.bits())
.await
.map_err(|e| ConnectionError::DbusOperation {
context: "registering secret agent with NetworkManager".into(),
source: e,
})?;
debug!(
"Registered secret agent '{}' with capabilities {:?}",
self.identifier, self.capabilities
);
let handle = SecretAgentHandle {
conn,
identifier: self.identifier,
capabilities: self.capabilities,
object_path: self.object_path,
cancel_rx,
store_rx,
};
Ok((handle, request_rx))
}
}
pub struct SecretAgentHandle {
conn: Connection,
identifier: String,
capabilities: SecretAgentCapabilities,
object_path: String,
cancel_rx: mpsc::UnboundedReceiver<CancelReason>,
store_rx: mpsc::UnboundedReceiver<SecretStoreEvent>,
}
impl std::fmt::Debug for SecretAgentHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SecretAgentHandle")
.field("identifier", &self.identifier)
.field("object_path", &self.object_path)
.finish_non_exhaustive()
}
}
impl SecretAgentHandle {
pub async fn reregister(&self) -> crate::Result<()> {
let proxy = AgentManagerProxy::new(&self.conn).await.map_err(|e| {
ConnectionError::DbusOperation {
context: "creating AgentManager proxy for re-registration".into(),
source: e,
}
})?;
proxy
.register_with_capabilities(&self.identifier, self.capabilities.bits())
.await
.map_err(|e| ConnectionError::DbusOperation {
context: "re-registering secret agent with NetworkManager".into(),
source: e,
})?;
debug!("Re-registered secret agent '{}'", self.identifier);
Ok(())
}
pub async fn unregister(self) -> crate::Result<()> {
let proxy = AgentManagerProxy::new(&self.conn).await.map_err(|e| {
ConnectionError::DbusOperation {
context: "creating AgentManager proxy for unregistration".into(),
source: e,
}
})?;
proxy
.unregister()
.await
.map_err(|e| ConnectionError::DbusOperation {
context: "unregistering secret agent".into(),
source: e,
})?;
self.conn
.release_name(&*self.identifier)
.await
.map_err(|e| ConnectionError::DbusOperation {
context: format!("releasing bus name '{}'", self.identifier),
source: e,
})?;
debug!("Unregistered secret agent '{}'", self.identifier);
Ok(())
}
pub fn cancellations(&mut self) -> &mut mpsc::UnboundedReceiver<CancelReason> {
&mut self.cancel_rx
}
pub fn store_events(&mut self) -> &mut mpsc::UnboundedReceiver<SecretStoreEvent> {
&mut self.store_rx
}
}