credibil_dwn/handlers/
protocols_query.rs

1//! # Protocols Query
2//!
3//! The protocols query endpoint handles `ProtocolsQuery` messages — requests
4//! to query the [`MessageStore`] for protocols configured for the DWN.
5
6use crate::authorization::Authorization;
7use crate::endpoint::{Message, Reply, Status};
8use crate::handlers::verify_grant;
9use crate::interfaces::Descriptor;
10use crate::interfaces::protocols::{Access, Configure, Query, QueryReply};
11use crate::provider::{MessageStore, Provider};
12use crate::store::ProtocolsQueryBuilder;
13use crate::{Result, utils};
14
15/// Handle — or process — a [`Query`] message.
16///
17/// # Errors
18///
19/// The endpoint will return an error when message authorization fails or when
20/// an issue occurs querying the [`MessageStore`].
21pub async fn handle(
22    owner: &str, query: Query, provider: &impl Provider,
23) -> Result<Reply<QueryReply>> {
24    // validate query
25    if let Some(filter) = &query.descriptor.filter {
26        utils::uri::validate(&filter.protocol)?;
27    }
28
29    // build actual query
30    let mut builder = ProtocolsQueryBuilder::new();
31    if let Some(filter) = &query.descriptor.filter {
32        builder = builder.protocol(&filter.protocol);
33    }
34
35    // unauthorized queries can only query for published protocols
36    if query.authorize(owner, provider).await? == Access::Published {
37        builder = builder.published(true);
38    }
39
40    let (records, cursor) = MessageStore::query(provider, owner, &builder.build()).await?;
41
42    // unpack messages
43    let mut entries = vec![];
44    for record in records {
45        entries.push(Configure::try_from(record)?);
46    }
47
48    Ok(Reply {
49        status: Status {
50            code: 200,
51            detail: Some("OK".to_string()),
52        },
53        body: Some(QueryReply {
54            entries: Some(entries),
55            cursor,
56        }),
57    })
58}
59
60impl Message for Query {
61    type Reply = QueryReply;
62
63    fn descriptor(&self) -> &Descriptor {
64        &self.descriptor.base
65    }
66
67    fn authorization(&self) -> Option<&Authorization> {
68        self.authorization.as_ref()
69    }
70
71    async fn handle(self, owner: &str, provider: &impl Provider) -> Result<Reply<Self::Reply>> {
72        handle(owner, self, provider).await
73    }
74}
75
76impl Query {
77    /// Check message has sufficient privileges.
78    async fn authorize(&self, owner: &str, store: &impl MessageStore) -> Result<Access> {
79        let Some(authzn) = &self.authorization else {
80            return Ok(Access::Published);
81        };
82
83        if authzn.author()? == owner {
84            return Ok(Access::Unpublished);
85        }
86
87        // does the message have a permission grant?
88        let Some(grant_id) = &authzn.payload()?.permission_grant_id else {
89            return Ok(Access::Published);
90        };
91
92        // verify permission grant
93        let grant = verify_grant::fetch_grant(owner, grant_id, store).await?;
94        grant.verify(owner, &authzn.signer()?, &self.descriptor.base, store).await?;
95
96        // if set, query and grant protocols need to match
97        let Some(protocol) = grant.data.scope.protocol() else {
98            return Ok(Access::Unpublished);
99        };
100        // has a grant but no filter: published protocols only
101        let Some(filter) = &self.descriptor.filter else {
102            return Ok(Access::Published);
103        };
104        // filter protocol must match grant protocol
105        if protocol != filter.protocol {
106            return Ok(Access::Published);
107        }
108
109        Ok(Access::Unpublished)
110    }
111}