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
30 changes: 16 additions & 14 deletions credentialsd-common/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,37 +266,39 @@ pub enum NfcState {
Failed(Error),
}

pub enum BackendRequest {
pub enum UserInteractedEvent {
/// Start Hybrid discovery
StartHybridDiscovery,
HybridDiscoveryRequested,

/// Start NFC discovery
StartNfcDiscovery,
NfcDiscoveryRequested,

/// Start USB discovery
StartUsbDiscovery,
UsbDiscoveryRequested,

/// Send client PIN
EnterClientPin(String),
ClientPinEntered(String),

/// Select a credential by credential ID
SelectCredential(String),
CredentialSelected(String),

CancelRequest,
RequestCancelled,
}

impl std::fmt::Debug for BackendRequest {
impl std::fmt::Debug for UserInteractedEvent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::StartHybridDiscovery => write!(f, "StartHybridDiscovery"),
Self::StartNfcDiscovery => write!(f, "StartNfcDiscovery"),
Self::StartUsbDiscovery => write!(f, "StartUsbDiscovery"),
Self::EnterClientPin(_) => f
Self::HybridDiscoveryRequested => write!(f, "StartHybridDiscovery"),
Self::NfcDiscoveryRequested => write!(f, "StartNfcDiscovery"),
Self::UsbDiscoveryRequested => write!(f, "StartUsbDiscovery"),
Self::ClientPinEntered(_) => f
.debug_tuple("EnterClientPin")
.field(&"******".to_string())
.finish(),
Self::SelectCredential(arg0) => f.debug_tuple("SelectCredential").field(arg0).finish(),
Self::CancelRequest => write!(f, "CancelRequest"),
Self::CredentialSelected(arg0) => {
f.debug_tuple("SelectCredential").field(arg0).finish()
}
Self::RequestCancelled => write!(f, "CancelRequest"),
}
}
}
Expand Down
212 changes: 117 additions & 95 deletions credentialsd-common/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use zvariant::{
SerializeDict, Signature, Str, Structure, StructureBuilder, Type, Value, signature::Fields,
};

use crate::model::{BackendRequest, Device, Operation, RequestId, RequestingApplication};
use crate::model::{Device, Operation, RequestId, RequestingApplication, UserInteractedEvent};

const TAG_VALUE_SIGNATURE: &Signature = &Signature::Structure(Fields::Static {
fields: &[&Signature::U32, &Signature::Variant],
Expand Down Expand Up @@ -49,12 +49,12 @@ const BACKGROUND_EVENT_ERROR_CREDENTIAL_EXCLUDED: u32 = 0x80000006;
const BACKGROUND_EVENT_ERROR_PIN_ATTEMPTS_EXHAUSTED: u32 = 0x80000007;
const BACKGROUND_EVENT_ERROR_PIN_NOT_SET: u32 = 0x80000008;

const BACKEND_REQUEST_START_HYBRID_DISCOVERY: u32 = 0x01;
const BACKEND_REQUEST_START_USB_DISCOVERY: u32 = 0x02;
const BACKEND_REQUEST_START_NFC_DISCOVERY: u32 = 0x03;
const BACKEND_REQUEST_ENTER_CLIENT_PIN: u32 = 0x04;
const BACKEND_REQUEST_SELECT_CREDENTIAL: u32 = 0x05;
const BACKEND_REQUEST_CANCEL_REQUEST: u32 = 0x06;
const USER_INTERACTED_EVENT_HYBRID_DISCOVERY_REQUESTED: u32 = 0x01;
const USER_INTERACTED_EVENT_NFC_DISCOVERY_REQUESTED: u32 = 0x02;
const USER_INTERACTED_EVENT_USB_DISCOVERY_REQUESTED: u32 = 0x03;
const USER_INTERACTED_EVENT_CLIENT_PIN_ENTERED: u32 = 0x04;
const USER_INTERACTED_EVENT_CREDENTIAL_SELECTED: u32 = 0x05;
const USER_INTERACTED_EVENT_REQUEST_CANCELLED: u32 = 0x06;

/// Flattened enum BackgroundEvent for sending across D-Bus.
#[derive(Debug, Clone, PartialEq)]
Expand Down Expand Up @@ -274,94 +274,6 @@ impl<'de> Deserialize<'de> for BackgroundEvent {
}
}

impl Type for BackendRequest {
const SIGNATURE: &'static Signature = TAG_VALUE_SIGNATURE;
}

impl From<&BackendRequest> for Structure<'_> {
fn from(value: &BackendRequest) -> Self {
match value {
BackendRequest::StartHybridDiscovery => tag_value_to_struct(0x01, None),
BackendRequest::StartNfcDiscovery => tag_value_to_struct(0x02, None),
BackendRequest::StartUsbDiscovery => tag_value_to_struct(0x03, None),
BackendRequest::EnterClientPin(pin) => {
tag_value_to_struct(0x04, Some(Value::Str(pin.into())))
}
BackendRequest::SelectCredential(credential_id) => {
tag_value_to_struct(0x05, Some(Value::Str(credential_id.into())))
}
BackendRequest::CancelRequest => tag_value_to_struct(0x06, None),
}
}
}

impl TryFrom<&Structure<'_>> for BackendRequest {
type Error = zvariant::Error;

fn try_from(value: &Structure<'_>) -> Result<Self, Self::Error> {
let (tag, value) = parse_tag_value_struct(value)?;

match tag {
0x01 => Ok(BackendRequest::StartHybridDiscovery),
0x02 => Ok(BackendRequest::StartNfcDiscovery),
0x03 => Ok(BackendRequest::StartUsbDiscovery),
0x04 => {
let s: Str = value.downcast_ref()?;
if s.is_empty() {
return Err(zvariant::Error::invalid_length(
s.len(),
&"a non-empty string",
));
}
Ok(BackendRequest::EnterClientPin(s.as_str().to_string()))
}
0x05 => {
let s: Str = value.downcast_ref()?;
if s.is_empty() {
return Err(zvariant::Error::invalid_length(
s.len(),
&"a non-empty string",
));
}
Ok(BackendRequest::SelectCredential(s.as_str().to_string()))
}
0x06 => Ok(BackendRequest::CancelRequest),
_ => Err(zvariant::Error::Message(format!(
"Unknown BackendRequest tag : {tag}"
))),
}
}
}

impl Serialize for BackendRequest {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let structure: Structure = self.into();
structure.serialize(serializer)
}
}

impl<'de> Deserialize<'de> for BackendRequest {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let d = Structure::deserializer_for_signature(TAG_VALUE_SIGNATURE).map_err(|err| {
D::Error::custom(format!(
"could not create deserializer for tag-value struct: {err}"
))
})?;
let structure = d.deserialize(deserializer)?;
(&structure).try_into().map_err(|err| {
D::Error::custom(format!(
"could not deserialize structure into BackendRequest: {err}"
))
})
}
}

#[derive(Clone, Debug, DeserializeDict, Type)]
#[zvariant(signature = "dict")]
pub struct CreateCredentialRequest {
Expand Down Expand Up @@ -507,6 +419,116 @@ impl From<GetPublicKeyCredentialResponse> for GetCredentialResponse {
}
}

impl Type for UserInteractedEvent {
const SIGNATURE: &'static Signature = TAG_VALUE_SIGNATURE;
}

impl From<&UserInteractedEvent> for Structure<'_> {
fn from(value: &UserInteractedEvent) -> Self {
match value {
UserInteractedEvent::HybridDiscoveryRequested => {
tag_value_to_struct(USER_INTERACTED_EVENT_HYBRID_DISCOVERY_REQUESTED, None)
}
UserInteractedEvent::NfcDiscoveryRequested => {
tag_value_to_struct(USER_INTERACTED_EVENT_NFC_DISCOVERY_REQUESTED, None)
}
UserInteractedEvent::UsbDiscoveryRequested => {
tag_value_to_struct(USER_INTERACTED_EVENT_USB_DISCOVERY_REQUESTED, None)
}
UserInteractedEvent::ClientPinEntered(pin) => tag_value_to_struct(
USER_INTERACTED_EVENT_CLIENT_PIN_ENTERED,
Some(Value::Str(pin.into())),
),
UserInteractedEvent::CredentialSelected(credential_id) => tag_value_to_struct(
USER_INTERACTED_EVENT_CREDENTIAL_SELECTED,
Some(Value::Str(credential_id.into())),
),
UserInteractedEvent::RequestCancelled => {
tag_value_to_struct(USER_INTERACTED_EVENT_REQUEST_CANCELLED, None)
}
}
}
}

impl TryFrom<&Structure<'_>> for UserInteractedEvent {
type Error = zvariant::Error;

fn try_from(value: &Structure<'_>) -> Result<Self, Self::Error> {
let (tag, value) = parse_tag_value_struct(value)?;

match tag {
USER_INTERACTED_EVENT_HYBRID_DISCOVERY_REQUESTED => {
Ok(UserInteractedEvent::HybridDiscoveryRequested)
}
USER_INTERACTED_EVENT_NFC_DISCOVERY_REQUESTED => {
Ok(UserInteractedEvent::NfcDiscoveryRequested)
}
USER_INTERACTED_EVENT_USB_DISCOVERY_REQUESTED => {
Ok(UserInteractedEvent::UsbDiscoveryRequested)
}
USER_INTERACTED_EVENT_CLIENT_PIN_ENTERED => {
let s: Str = value.downcast_ref()?;
if s.is_empty() {
return Err(zvariant::Error::invalid_length(
s.len(),
&"a non-empty string",
));
}
Ok(UserInteractedEvent::ClientPinEntered(
s.as_str().to_string(),
))
}
USER_INTERACTED_EVENT_CREDENTIAL_SELECTED => {
let s: Str = value.downcast_ref()?;
if s.is_empty() {
return Err(zvariant::Error::invalid_length(
s.len(),
&"a non-empty string",
));
}
Ok(UserInteractedEvent::CredentialSelected(
s.as_str().to_string(),
))
}
USER_INTERACTED_EVENT_REQUEST_CANCELLED => Ok(UserInteractedEvent::RequestCancelled),
_ => Err(zvariant::Error::Message(format!(
"Unknown {} tag : {tag}",
stringify!(UserInteractedEvent)
))),
}
}
}

impl Serialize for UserInteractedEvent {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let structure: Structure = self.into();
structure.serialize(serializer)
}
}

impl<'de> Deserialize<'de> for UserInteractedEvent {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let d = Structure::deserializer_for_signature(TAG_VALUE_SIGNATURE).map_err(|err| {
D::Error::custom(format!(
"could not create deserializer for tag-value struct: {err}"
))
})?;
let structure = d.deserialize(deserializer)?;
(&structure).try_into().map_err(|err| {
D::Error::custom(format!(
"could not deserialize structure into {}: {err}",
stringify!(UserInteractedEvent)
))
})
}
}

#[derive(Clone, Debug, Serialize, Deserialize, Type)]
pub struct ViewRequest {
pub operation: Operation,
Expand Down
19 changes: 10 additions & 9 deletions credentialsd-ui/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use async_std::{
};
use credentialsd_common::{
client::FlowController,
model::{BackendRequest, RequestId},
model::{RequestId, UserInteractedEvent},
server::BackgroundEvent,
};
use futures_lite::StreamExt;
Expand Down Expand Up @@ -129,34 +129,35 @@ impl FlowController for DbusCredentialClient {

#[derive(Debug)]
pub struct FlowControlClient {
pub tx: Sender<BackendRequest>,
pub tx: Sender<UserInteractedEvent>,
pub rx: AsyncMutex<Option<Receiver<BackgroundEvent>>>,
}

impl FlowControlClient {
pub async fn discover_hybrid_authenticators(&self) -> Result<(), ()> {
self.send(BackendRequest::StartHybridDiscovery).await
self.send(UserInteractedEvent::HybridDiscoveryRequested)
.await
}

pub async fn discover_nfc_authenticators(&mut self) -> Result<(), ()> {
self.send(BackendRequest::StartNfcDiscovery).await
self.send(UserInteractedEvent::NfcDiscoveryRequested).await
}

pub async fn discover_usb_authenticators(&mut self) -> Result<(), ()> {
self.send(BackendRequest::StartUsbDiscovery).await
self.send(UserInteractedEvent::UsbDiscoveryRequested).await
}

pub async fn enter_client_pin(&mut self, pin: String) -> Result<(), ()> {
self.send(BackendRequest::EnterClientPin(pin)).await
self.send(UserInteractedEvent::ClientPinEntered(pin)).await
}

pub async fn select_credential(&self, credential_id: String) -> Result<(), ()> {
self.send(BackendRequest::SelectCredential(credential_id))
self.send(UserInteractedEvent::CredentialSelected(credential_id))
.await
}

pub async fn cancel_request(&self) -> Result<(), ()> {
self.send(BackendRequest::CancelRequest).await
self.send(UserInteractedEvent::RequestCancelled).await
}

/// Returns a channel for background events.
Expand All @@ -167,7 +168,7 @@ impl FlowControlClient {
})
}

async fn send(&self, request: BackendRequest) -> Result<(), ()> {
async fn send(&self, request: UserInteractedEvent) -> Result<(), ()> {
match self.tx.send(request).await {
Ok(_) => Ok(()),
Err(_) => Err(()),
Expand Down
21 changes: 13 additions & 8 deletions credentialsd-ui/src/dbus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use zbus::{
use credentialsd_common::{
client::FlowController,
model::{
BackendRequest, Device, Operation, PortalBackendOptions, RequestId, RequestingApplication,
Device, Operation, PortalBackendOptions, RequestId, RequestingApplication,
UserInteractedEvent,
},
server::{BackgroundEvent, ViewRequest, WindowHandle},
};
Expand Down Expand Up @@ -89,16 +90,20 @@ impl UiControlService {
while let Ok(msg) = fc_rx.recv().await {
// UI doesn't get an error if these fail...
let result = match &msg {
BackendRequest::StartHybridDiscovery => client.get_hybrid_credential().await,
BackendRequest::StartNfcDiscovery => client.get_nfc_credential().await,
BackendRequest::StartUsbDiscovery => client.get_usb_credential().await,
BackendRequest::EnterClientPin(pin) => {
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
}
BackendRequest::SelectCredential(cred_id) => {
UserInteractedEvent::CredentialSelected(cred_id) => {
client.select_credential(cred_id.to_string()).await
}
BackendRequest::CancelRequest => client.cancel_request(request.id).await,
UserInteractedEvent::RequestCancelled => {
client.cancel_request(request.id).await
}
};
if let Err(err) = result {
tracing::error!("Failed to send {msg:?} to frontend: {err:?}");
Expand Down Expand Up @@ -292,6 +297,6 @@ impl CeremonyObject {
#[zbus(signal)]
async fn user_interacted(
emitter: SignalEmitter<'_>,
event: &BackendRequest,
event: &UserInteractedEvent,
) -> zbus::Result<()>;
}
Loading
Loading