credibil_dwn/
endpoint.rs

1//! # Endpoint
2//!
3//! `Endpoint` provides the entry point for DWN messages. Messages are routed
4//! to the appropriate handler for processing, returning a reply that can be
5//! serialized to a JSON object.
6
7use std::fmt::Debug;
8
9use serde::{Deserialize, Serialize};
10
11use crate::authorization::Authorization;
12use crate::interfaces::Descriptor;
13use crate::provider::Provider;
14use crate::{Result, schema, unauthorized};
15
16/// Handle incoming messages.
17///
18/// # Errors
19///
20/// This method can fail for a number of reasons related to the imcoming
21/// message's viability. Expected failues include invalid authorization,
22/// insufficient permissions, and invalid message content.
23///
24/// Implementers should look to the Error type and description for more
25/// information on the reason for failure.
26pub async fn handle<T>(
27    owner: &str, message: impl Message<Reply = T>, provider: &impl Provider,
28) -> Result<Reply<T>> {
29    message.validate(owner, provider).await?;
30    message.handle(owner, provider).await
31}
32
33/// Methods common to all messages.
34///
35/// The primary role of this trait is to provide a common interface for
36/// messages so they can be handled by [`handle`] method.
37pub trait Message: Serialize + Clone + Debug + Send + Sync {
38    /// The inner reply type specific to the implementing message.
39    type Reply;
40
41    /// Returns message descriptor properties common to all messages (i.e.,
42    /// `interface`, `method`, and `message_timestamp`).
43    fn descriptor(&self) -> &Descriptor;
44
45    /// Returns the messages's authorization, when set.
46    fn authorization(&self) -> Option<&Authorization>;
47
48    /// Routes the message to the concrete handler used to process the message.
49    fn handle(
50        self, owner: &str, provider: &impl Provider,
51    ) -> impl Future<Output = Result<Reply<Self::Reply>>> + Send;
52
53    /// Perform initial validation of the message.
54    ///
55    /// Validation undertaken here is common to all messages, with message-
56    /// specific validation performed by the message's handler.
57    fn validate(
58        &self, _owner: &str, provider: &impl Provider,
59    ) -> impl Future<Output = Result<()>> + Send {
60        async {
61            // if !tenant_gate.active(owner)? {
62            //     return Err(Error::Unauthorized("tenant not active"));
63            // }
64
65            // validate the message schema during development
66            #[cfg(debug_assertions)]
67            schema::validate(self)?;
68
69            // authenticate the requestor
70            if let Some(authzn) = self.authorization() {
71                if let Err(e) = authzn.verify(provider.clone()).await {
72                    return Err(unauthorized!("failed to authenticate: {e}"));
73                }
74            }
75
76            Ok(())
77        }
78    }
79}
80
81/// Top-level reply data structure common to all handler.
82#[derive(Debug, Default, Deserialize, Serialize)]
83pub struct Reply<ReplyBody> {
84    /// The status message to accompany the reply.
85    pub status: Status,
86
87    /// The endpoint-specific reply.
88    #[serde(skip_serializing_if = "Option::is_none")]
89    #[serde(flatten)]
90    pub body: Option<ReplyBody>,
91}
92
93/// Reply status.
94#[derive(Clone, Debug, Default, Deserialize, Serialize)]
95#[serde(rename_all = "camelCase")]
96pub struct Status {
97    /// Status code.
98    pub code: u16,
99
100    /// Status detail.
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub detail: Option<String>,
103}
104
105// impl Message for Subscribe {
106//     type Reply = SubscribeReply;
107
108//     fn descriptor(&self) -> &Descriptor {
109//         &self.descriptor.base
110//     }
111
112//     fn authorization(&self) -> Option<&Authorization> {
113//         Some(&self.authorization)
114//     }
115
116//     async fn handle(self, owner: &str, provider: &impl Provider) -> Result<Reply<Self::Reply>> {
117//         messages::subscribe::handle(owner, self, provider).await
118//     }
119// }