Skip to main content

ocm_types/
discovery.rs

1// SPDX-FileCopyrightText: 2026 Matthias Kraus <info@opengeomesh.org>
2//
3// SPDX-License-Identifier: LGPL-3.0-or-later
4
5use std::collections::HashMap;
6
7use serde::{Deserialize, Serialize};
8
9use crate::common::ShareType;
10
11/// This is the response payload of the discovery endpoint, representing the
12/// properties and capabilities offered by this server.
13#[allow(rustdoc::bare_urls)]
14#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct Discovery {
17    /// Whether the OCM service is enabled at this endpoint
18    pub enabled: bool,
19    /// The OCM API version this endpoint supports
20    pub api_version: String,
21    /// The URI of the OCM API available at this endpoint
22    /// Example: https://cloud.example.org/ocm
23    pub end_point: String,
24    /// A friendly branding name of this endpoint
25    /// Example: MyCloudStorage
26    #[serde(skip_serializing_if = "Option::is_none", default)]
27    pub provider: Option<String>,
28    /// A list of all supported resource types with their access protocols.
29    /// Each resource type is identified by its `name`: the list MUST NOT
30    /// contain more than one resource type object per given `name`.
31    pub resource_types: Vec<Resource>,
32    /// The optional capabilities exposed at this endpoint according to the
33    /// present specifications.
34    /// ```
35    /// use ocm_types::discovery::Capability;
36    ///
37    /// let capabilities = vec![
38    ///     Capability::WebdavUri,
39    ///     Capability::ProtocolObject,
40    /// ];
41    /// let json: Vec<Capability> = serde_json::from_str(r#"[
42    ///     "webdav-uri",
43    ///     "protocol-object"
44    /// ]"#).unwrap();
45    /// assert_eq!(capabilities, json);
46    /// ```
47    #[serde(skip_serializing_if = "Option::is_none", default)]
48    pub capabilities: Option<Vec<Capability>>,
49    /// The criteria for accepting a Share Creation Notification.
50    /// As all Receiving Servers should require the use of TLS in API calls,
51    /// it is not necessary to expose that as a criterium.
52    ///
53    /// example:
54    /// - allowlist
55    /// - invite
56    /// ```
57    /// use ocm_types::discovery::Criterium;
58    ///
59    /// let criteria = vec![
60    ///     Criterium::Allowlist,
61    ///     Criterium::Invite,
62    /// ];
63    /// let json: Vec<Criterium> = serde_json::from_str(r#"[
64    ///     "allowlist",
65    ///     "invite"
66    /// ]"#).unwrap();
67    /// assert_eq!(criteria, json);
68    /// ```
69    #[serde(skip_serializing_if = "Option::is_none", default)]
70    pub criteria: Option<Vec<Criterium>>,
71    /// The signatory used to sign outgoing request to confirm its origin.
72    /// The signatory is optional but it MUST contain `id` and `publicKeyPem`.
73    #[deprecated(
74        note = "Use public keys at `/.well-known/jwks.json` instead for RFC 9421 support"
75    )]
76    #[serde(skip_serializing_if = "Option::is_none", default)]
77    pub public_key: Option<PublicKey>,
78    /// Optional URL path of a web page where a user can accept an invite, when
79    /// query parameters "token" and "providerDomain" are provided.
80    /// Implementations that offer the invites capability SHOULD provide this URL
81    /// as well in order to enhance the UX of the Invite Flow. If for example
82    /// "/index.php/apps/sciencemesh/accept" is specified here then a
83    /// Where-Are-You-From page could redirect the end-user to
84    /// /index.php/apps/sciencemesh/accept?token=zi5kooKu3ivohr9a&providerDomain=example.com.
85    #[serde(skip_serializing_if = "Option::is_none", default)]
86    pub invite_accept_dialog: Option<String>,
87    /// URL of the token endpoint where the Sending Server can exchange a refreshToken
88    /// for a short-lived bearer token.
89    /// Implementations that offer the [Capability::ExchangeToken] MUST provide this URL
90    /// as well.
91    // TODO enforce token_end_point to be available when ExchangeToken Capability is advertised?
92    pub token_end_point: Option<String>,
93}
94
95#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
96pub struct ApiVersion(pub String);
97
98#[allow(rustdoc::bare_urls)]
99#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)]
100#[serde(rename_all = "camelCase")]
101pub struct PublicKey {
102    /// unique id of the key in URI format. The hostname set the origin
103    /// of the request and MUST be identical to the current discovery endpoint.
104    /// example: https://my-cloud-storage.org/ocm#signature
105    pub key_id: String,
106    /// PEM-encoded version of the public key.
107    /// example:
108    ///  -----BEGIN PUBLIC KEY-----
109    ///     MII...QDD
110    ///  -----END PUBLIC KEY-----
111    pub public_key_pem: String,
112}
113
114#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
115#[serde(rename_all = "kebab-case")]
116pub enum Criterium {
117    /// to indicate that API requests
118    /// without http signatures will be rejected.
119    HttpRequestSignatures,
120    /// To indicate that API requests without token exchange will be rejected.
121    TokenExchange,
122    /// some servers MAY be blocked based on their address
123    Denylist,
124    /// unknown servers MAY be blocke
125    Allowlist,
126    /// an invite MUST have been exchanged between the
127    /// sender and the receiver before a [crate::share::NewShare] can be
128    /// sent
129    Invite,
130}
131
132#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
133#[serde(rename_all = "kebab-case")]
134pub enum Capability {
135    /// to indicate that this OCM Server can apply a Sending Server's MFA requirements for
136    /// a Share on their behalf.
137    EnforceMfa,
138    /// to indicate that this OCM Server exposes a [RFC6749](https://datatracker.ietf.org/doc/rfc6749/)-compliant
139    /// endpoint, which allows to exchange a secret received in the [crate::share::WebDavProperties] of a
140    /// [crate::share::NewShare] for a short-lived bearer token.
141    ExchangeToken,
142    /// to indicate that this OCM Server supports
143    /// [RFC9421] HTTP Message Signatures and advertises public keys in the
144    /// format specified by [RFC7517] at the `/.well-known/jwks.json`
145    /// endpoint for signature verification.
146    HttpSig,
147    /// to indicate the server would support acting as an Invite Sender or Invite Receiver
148    /// OCM Server. This might be useful for suggesting to a user that existing contacts
149    /// might be upgraded to the more secure (and possibly required) invite flow.
150    Invites,
151    /// to indicate that this OCM Server exposes a WAYF Page to facilitate the Invite flow.
152    InviteWayf,
153    /// to indicate that this OCM Server handles
154    /// notifications to exchange updates on shares and invites.
155    Notifications,
156    /// to indicate that this OCM Server can receive a Share Creation Notification whose
157    /// protocol object contains one property per supported protocol instead of containing
158    /// the standard name and options properties.
159    ProtocolObject,
160    /// to indicate that this OCM Server can append a relative URI to the path listed for
161    /// WebDAV in the appropriate resourceTypes entry
162    WebdavUri,
163}
164
165#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
166#[serde(rename_all = "camelCase")]
167pub struct Resource {
168    /// A supported resource type (file, folder, calendar, contact,
169    /// ...).
170    /// Implementations MUST support `file` at a minimum.
171    /// Example: file
172    pub name: String,
173    /// The supported recipient share types.
174    /// Implementations MUST support `user` at a minimum.
175    /// Example: user
176    pub share_types: Vec<ShareType>,
177    /// The supported protocols to access shared resources at this
178    /// endpoint.
179    /// Implementations MUST support at least `webdav` for `file` resources,
180    /// any other combination of resources and protocols is optional.
181    ///
182    // properties:
183    //   webdav:
184    //     type: string
185    //     description: >
186    //       The top-level WebDAV path at this endpoint. In order to
187    //       access
188    //       a remote shared resource, implementations SHOULD
189
190    //     use this path
191    //       as a prefix (see sharing examples).
192    //   webapp:
193    //     type: string
194    //     description: >
195    //       The top-level path for web apps at this endpoint. In order
196    //       to
197    //       access a remote web app, implementations SHOULD use this path
198    //       as a prefix (see sharing examples).
199    //   datatx:
200    //     type: string
201    //     description: >
202    //       The top-level path to be used for data transfers. In order
203    //       to
204    //       access a remote shared resource, implementations SHOULD use
205    //       this path as a prefix (see sharing examples). In addition,
206    //       implementations are expected to execute the transfer using
207    //       WebDAV as the wire protocol.
208    // patternProperties:
209    //   ^.*$:
210    //     type: string
211    //     description: >
212    //       Any additional protocol supported for this resource type
213    //       MAY
214    //       be advertised here, where the value MAY correspond to a top-level
215    //       URI to be used for that protocol.
216    /// ```
217    /// use std::collections::HashMap;
218    ///
219    /// let resources = HashMap::from_iter(vec![
220    ///     ("webdav".to_string(), "/remote/dav/ocm/".to_string()),
221    ///     ("webapp".to_string(), "/apps/ocm/".to_string()),
222    ///     ("talk".to_string(), "/apps/speed/api/".to_string()),
223    ///     ("ssh".to_string(), "example.org:2222".to_string())
224    /// ].into_iter());
225    ///
226    /// let json: HashMap<String,String> = serde_json::from_str(r#"{
227    ///     "webdav": "/remote/dav/ocm/",
228    ///     "webapp": "/apps/ocm/",
229    ///     "talk": "/apps/speed/api/",
230    ///     "ssh": "example.org:2222"
231    /// }"#).unwrap();
232    /// assert_eq!(resources, json);
233    /// ```
234    pub protocols: HashMap<String, String>,
235}