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