Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 1 addition & 124 deletions credentialsd-ui/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,131 +1,8 @@
use async_std::{
channel::{Receiver, Sender},
stream::Stream,
sync::Mutex as AsyncMutex,
};
use credentialsd_common::{
client::FlowController,
model::{RequestId, UserInteractedEvent},
server::BackgroundEvent,
};
use futures_lite::StreamExt;
use zbus::Connection;

use crate::dbus::FlowControlServiceProxy;

pub struct DbusCredentialClient {
conn: Connection,
}

impl DbusCredentialClient {
pub fn new(conn: Connection) -> Self {
Self { conn }
}
async fn proxy(&self) -> std::result::Result<FlowControlServiceProxy<'_>, ()> {
FlowControlServiceProxy::new(&self.conn)
.await
.map_err(|err| tracing::error!("Failed to communicate with D-Bus service: {err}"))
}
}

impl FlowController for DbusCredentialClient {
async fn get_available_public_key_devices(
&self,
) -> std::result::Result<Vec<credentialsd_common::model::Device>, ()> {
self.proxy()
.await?
.get_available_public_key_devices()
.await
.map_err(|err| {
tracing::error!("Failed to retrieve available devices/transports: {err}")
})
}

async fn get_hybrid_credential(&mut self) -> std::result::Result<(), ()> {
self.proxy()
.await?
.get_hybrid_credential()
.await
.inspect_err(|err| tracing::error!("Failed to start hybrid credential flow: {err}"))
.map_err(|_| ())
}

async fn get_usb_credential(&mut self) -> std::result::Result<(), ()> {
self.proxy()
.await?
.get_usb_credential()
.await
.inspect_err(|err| tracing::error!("Failed to start USB credential flow: {err}"))
.map_err(|_| ())
}

async fn get_nfc_credential(&mut self) -> std::result::Result<(), ()> {
self.proxy()
.await?
.get_nfc_credential()
.await
.inspect_err(|err| tracing::error!("Failed to start NFC credential flow: {err}"))
.map_err(|_| ())
}

async fn subscribe(
&mut self,
) -> std::result::Result<
std::pin::Pin<
Box<dyn Stream<Item = credentialsd_common::server::BackgroundEvent> + Send + 'static>,
>,
(),
> {
let stream = self
.proxy()
.await?
.receive_state_changed()
.await
.map_err(|err| tracing::error!("Failed to initalize event stream: {err}"))?
.filter_map(|msg| {
msg.args()
.map(|args| args.update)
.inspect_err(|err| tracing::warn!("Failed to parse StateChanged signal: {err}"))
.ok()
})
.boxed();
self.proxy()
.await?
.subscribe()
.await
.map_err(|err| tracing::error!("Failed to initialize event stream: {err}"))
.map(|_| stream)
}

async fn enter_client_pin(&mut self, pin: String) -> std::result::Result<(), ()> {
self.proxy()
.await?
.enter_client_pin(pin)
.await
.map_err(|err| tracing::error!("Failed to send PIN to authenticator: {err}"))
}

async fn select_credential(&self, credential_id: String) -> std::result::Result<(), ()> {
self.proxy()
.await?
.select_credential(credential_id)
.await
.map_err(|err| tracing::error!("Failed to select credential: {err}"))
}

async fn cancel_request(&self, request_id: RequestId) -> Result<(), ()> {
if self
.proxy()
.await?
.cancel_request(request_id)
.await
.is_err()
{
tracing::warn!("Failed to cancel request {request_id}");
}
Ok(())
}
}
use credentialsd_common::{model::UserInteractedEvent, server::BackgroundEvent};

#[derive(Debug)]
pub struct FlowControlClient {
Expand Down
105 changes: 3 additions & 102 deletions credentialsd-ui/src/dbus.rs
Original file line number Diff line number Diff line change
@@ -1,126 +1,27 @@
use std::sync::Arc;

use async_std::{
channel::{self, Receiver, Sender},
stream::StreamExt,
channel::{self, Sender},
sync::Mutex as AsyncMutex,
task::JoinHandle,
};
use zbus::{
Connection, ObjectServer, fdo, interface,
ObjectServer, fdo, interface,
message::Header,
names::{BusName, OwnedUniqueName},
object_server::SignalEmitter,
proxy,
zvariant::{ObjectPath, Optional},
};

use credentialsd_common::{
client::FlowController,
model::{
Device, Operation, PortalBackendOptions, RequestId, RequestingApplication,
UserInteractedEvent,
},
server::{BackgroundEvent, ViewRequest, WindowHandle},
};

use crate::client::{DbusCredentialClient, FlowControlClient};

#[proxy(
gen_blocking = false,
interface = "xyz.iinuwa.credentialsd.FlowControl1",
default_path = "/xyz/iinuwa/credentialsd/FlowControl",
default_service = "xyz.iinuwa.credentialsd.FlowControl"
)]
pub trait FlowControlService {
async fn subscribe(&self) -> fdo::Result<()>;

async fn get_available_public_key_devices(&self) -> fdo::Result<Vec<Device>>;

async fn get_hybrid_credential(&self) -> fdo::Result<()>;

async fn get_usb_credential(&self) -> fdo::Result<()>;
async fn get_nfc_credential(&self) -> fdo::Result<()>;

async fn select_device(&self, device_id: String) -> fdo::Result<()>;
async fn enter_client_pin(&self, pin: String) -> fdo::Result<()>;
async fn select_credential(&self, credential_id: String) -> fdo::Result<()>;
async fn cancel_request(&self, request_id: RequestId) -> fdo::Result<()>;

#[zbus(signal)]
async fn state_changed(update: BackgroundEvent) -> zbus::Result<()>;
}

pub struct UiControlService {
pub request_tx: Sender<(ViewRequest, Arc<AsyncMutex<FlowControlClient>>)>,
}

/// These methods are called by the credential service to control the UI.
#[interface(name = "xyz.iinuwa.credentialsd.UiControl1")]
impl UiControlService {
async fn launch_ui(
&self,
#[zbus(connection)] conn: &Connection,
request: ViewRequest,
) -> fdo::Result<()> {
tracing::debug!("Received UI launch request");
let mut client = DbusCredentialClient::new(conn.clone());
let (fc_tx, fc_rx) = async_std::channel::unbounded();
let (bg_tx, bg_rx) = async_std::channel::unbounded();
match client.subscribe().await {
Ok(mut bg_event_stream) => async_std::task::spawn(async move {
while let Some(bg_event) = bg_event_stream.next().await {
if let Err(_) = bg_tx.send(bg_event).await {
tracing::debug!("Background event receiver dropped. Stopping.");
break;
}
}
}),
Err(_) => {
tracing::error!(
?request,
"Failed to subscribe to background events for request"
);
return Err(fdo::Error::Failed(
"Failed to subscribe to background events for request".to_string(),
));
}
};
async_std::task::spawn(async move {
while let Ok(msg) = fc_rx.recv().await {
// UI doesn't get an error if these fail...
let result = match &msg {
UserInteractedEvent::HybridDiscoveryRequested => {
client.get_hybrid_credential().await
}
UserInteractedEvent::NfcDiscoveryRequested => client.get_nfc_credential().await,
UserInteractedEvent::UsbDiscoveryRequested => client.get_usb_credential().await,
UserInteractedEvent::ClientPinEntered(pin) => {
client.enter_client_pin(pin.to_string()).await
}
UserInteractedEvent::CredentialSelected(cred_id) => {
client.select_credential(cred_id.to_string()).await
}
UserInteractedEvent::RequestCancelled => {
client.cancel_request(request.id).await
}
};
if let Err(err) = result {
tracing::error!("Failed to send {msg:?} to frontend: {err:?}");
}
}
client
});
let flow_control_client = FlowControlClient {
tx: fc_tx,
rx: AsyncMutex::new(Some(bg_rx)),
};
self.request_tx
.send((request, Arc::new(AsyncMutex::new(flow_control_client))))
.await
.map_err(|_| fdo::Error::Failed("UI failed to launch".to_string()))
}
}
use crate::client::FlowControlClient;

pub struct CredentialPortalBackend {
pub request_tx: Sender<(ViewRequest, Arc<AsyncMutex<FlowControlClient>>)>,
Expand Down
4 changes: 1 addition & 3 deletions credentialsd-ui/src/gui/view_model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ use gettextrs::gettext;
use serde::{Deserialize, Serialize};
use tracing::{error, info};

use credentialsd_common::model::{
Device, Error, HybridState, NfcState, Operation, Transport, UsbState, ViewUpdate,
};
use credentialsd_common::model::{Device, HybridState, Operation, Transport, ViewUpdate};

use crate::client::FlowControlClient;

Expand Down
7 changes: 1 addition & 6 deletions credentialsd-ui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ mod gui;

use std::error::Error;

use crate::dbus::{CredentialPortalBackend, UiControlService};
use crate::dbus::CredentialPortalBackend;

fn main() -> Result<(), Box<dyn Error>> {
tracing_subscriber::fmt::init();
Expand All @@ -23,15 +23,10 @@ async fn run() -> Result<(), Box<dyn Error>> {
println!(" ✅");

print!("Starting UI Control listener...\t");
let interface = UiControlService {
request_tx: request_tx.clone(),
};
let portal_backend_interface = CredentialPortalBackend { request_tx };
let path = "/xyz/iinuwa/credentialsd/UiControl";
let service = "xyz.iinuwa.credentialsd.UiControl";
let _server_conn = zbus::connection::Builder::session()?
.name(service)?
.serve_at(path, interface)?
.serve_at("/org/freedesktop/portal/desktop", portal_backend_interface)?
.build()
.await?;
Expand Down
Loading
Loading