ocm_types/share.rs
1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::{Value, json};
5
6use crate::common::ShareType;
7
8#[derive(Debug, PartialEq, Eq, Default, Clone, Serialize, Deserialize)]
9#[serde(rename_all = "camelCase")]
10pub struct NewShare {
11 /// Consumer specific identifier of the user, group or federation the provider
12 /// wants to share the resource with. This is known in advance.
13 /// Please note that the consumer service endpoint is known in advance
14 /// as well, so this is no part of the request body.
15 /// example: 51dc30ddc473d43a6011e9ebba6ca770@geant.org
16 pub share_with: String,
17 /// Name of the resource (file or folder).
18 /// example: resource.txt
19 pub name: String,
20 /// Optional description of the resource (file or folder).
21 #[serde(skip_serializing_if = "Option::is_none", default)]
22 pub description: Option<String>,
23 /// Identifier to identify the shared resource at the provider side.
24 /// This is unique per provider such that if the same resource is shared twice,
25 /// this providerId will not be repeated.
26 ///
27 /// example: 7c084226-d9a1-11e6-bf26-cec0c932ce01
28 pub provider_id: String,
29 /// Provider specific identifier of the user who owns the resource.
30 ///
31 /// example: 6358b71804dfa8ab069cf05ed1b0ed2a@apiwise.nl
32 pub owner: String,
33 /// Provider specific identifier of the user that wants to share the
34 /// resource. Please note that the requesting provider is being
35 /// identified on a higher level, so the former `remote` property
36 /// is not part of the request body.
37 ///
38 /// example: 527bd5b5d689e2c32ae974c6229ff785@apiwise.nl
39 pub sender: String,
40 /// Display name of the owner of the resource
41 /// example: Dimitri
42 #[serde(skip_serializing_if = "Option::is_none", default)]
43 pub owner_display_name: Option<String>,
44 /// Display name of the user that wants to share the resource
45 /// example: John Doe
46 #[serde(skip_serializing_if = "Option::is_none", default)]
47 pub sender_display_name: Option<String>,
48 /// Recipient share type
49 pub share_type: ShareType,
50 /// Resource type (file, calendar, contact, ...)
51 /// example: file
52 pub resource_type: String,
53 /// The expiration time for the share, in seconds of UTC time since
54 /// Unix epoch. If omitted, it is assumed that the share does not expire.
55 #[serde(skip_serializing_if = "Option::is_none", default)]
56 pub expiration: Option<i64>,
57 /// A nonce to be exchanged for a (potentially short-lived) bearer token
58 /// at the Sending Server's `/token` endpoint.
59 #[serde(skip_serializing_if = "Option::is_none", default)]
60 pub code: Option<String>,
61 pub protocol: Protocol,
62}
63
64/// The Sending Server
65/// * holds the Resource ("file server" or "Entreprise File Sync and Share (EFSS) server" role), provides access to it (by exposing at least one "API"),
66/// * takes the decision to create the Share based on user interface gestures from the Sending Party (the "Authorization Server" role in OAuth)
67/// * takes the decision about authorizing attempts to access the Resource (the "Resource Server" role in OAuth)
68/// * sends out Share Creation Notifications when appropriate
69#[derive(Debug, Clone, PartialEq, Eq, Hash)]
70pub struct SendingServer(String);
71
72impl NewShare {
73 /// Get the Sending Server from the sender of the share.
74 pub fn sending_server(&self) -> Option<SendingServer> {
75 self.sender
76 .split("@")
77 .last()
78 .and_then(|host| host.split(&[':', '/'][..]).next())
79 .map(|fqdn: &str| fqdn.into())
80 }
81}
82
83impl<T: Into<String>> From<T> for SendingServer {
84 fn from(value: T) -> Self {
85 // FIXME make sure this is a fqdn
86 Self(value.into())
87 }
88}
89
90/// JSON object with specific options for each protocol.
91///
92/// The supported protocols are:
93/// - `webdav`, to access the data
94/// - `webapp`, to access remote web applications
95/// - `datatx`, to transfer the data to the remote endpoint
96///
97/// Other custom protocols might be added in the future.
98///
99/// # Single Protocol Legacy
100/// ```
101/// use ocm_types::share::Protocol;
102/// use std::collections::HashMap;
103/// use serde_json::Value;
104/// use serde_json::json;
105///
106/// #[allow(deprecated)]
107/// let single_protocol_legacy = Protocol{
108/// name: "webdav".to_string(),
109/// options: Some(HashMap::from_iter(vec![
110/// ("sharedSecret".to_string(), Value::String("hfiuhworzwnur98d3wjiwhr".to_string())),
111/// ("permissions".to_string(), Value::String("some permissions scheme".to_string())),
112/// ].into_iter())),
113/// ..Default::default()
114/// };
115/// let json: Value = serde_json::from_str(r#"{
116/// "name": "webdav",
117/// "options": {
118/// "sharedSecret": "hfiuhworzwnur98d3wjiwhr",
119/// "permissions": "some permissions scheme"
120/// }
121/// }"#).unwrap();
122/// assert_eq!(json!(single_protocol_legacy), json);
123/// ```
124///
125/// # Single Protocol New
126/// ```
127/// use ocm_types::share::Protocol;
128/// use ocm_types::share::WebDavProperties;
129/// use ocm_types::share::WebDavPermissions;
130/// use std::collections::HashMap;
131/// use serde_json::Value;
132/// use serde_json::json;
133///
134/// let single_protocol_new = Protocol{
135/// name: "multi".to_string(),
136/// webdav: Some(WebDavProperties{
137/// uri: "7c084226-d9a1-11e6-bf26-cec0c932ce01".to_string(),
138/// shared_secret: Some("hfiuhworzwnur98d3wjiwhr".to_string()),
139/// permissions: vec![
140/// WebDavPermissions::Read,
141/// WebDavPermissions::Write,
142/// ],
143/// requirements: vec![]
144/// }),
145/// ..Default::default()
146/// };
147/// let json: Value = serde_json::from_str(r#"{
148/// "name": "multi",
149/// "webdav": {
150/// "uri": "7c084226-d9a1-11e6-bf26-cec0c932ce01",
151/// "sharedSecret": "hfiuhworzwnur98d3wjiwhr",
152/// "permissions": ["read", "write"]
153/// }
154/// }"#).unwrap();
155/// assert_eq!(json!(single_protocol_new), json);
156/// ```
157///
158/// # Multi Protocol
159/// ```
160/// use ocm_types::share::Protocol;
161/// use ocm_types::share::WebDavProperties;
162/// use ocm_types::share::WebDavPermissions;
163/// use ocm_types::share::WebDavRequirements;
164/// use ocm_types::share::WebAppProperties;
165/// use ocm_types::share::WebAppViewMode;
166/// use ocm_types::share::DataTxProperties;
167/// use std::collections::HashMap;
168/// use serde_json::Value;
169/// use serde_json::json;
170///
171/// let single_protocol_new = Protocol{
172/// name: "multi".to_string(),
173/// webdav: Some(WebDavProperties{
174/// uri: "7c084226-d9a1-11e6-bf26-cec0c932ce01".to_string(),
175/// shared_secret: Some("hfiuhworzwnur98d3wjiwhr".to_string()),
176/// permissions: vec![
177/// WebDavPermissions::Read,
178/// WebDavPermissions::Write
179/// ],
180/// requirements: vec![
181/// WebDavRequirements::MfaEnforced
182/// ]
183/// }),
184/// webapp: Some(WebAppProperties{
185/// uri: "7c084226-d9a1-11e6-bf26-cec0c932ce01".to_string(),
186/// shared_secret: Some("hfiuhworzwnur98d3wjiwhr".to_string()),
187/// view_mode: WebAppViewMode::Read
188/// }),
189/// datatx: Some(DataTxProperties{
190/// src_uri: "7c084226-d9a1-11e6-bf26-cec0c932ce01".to_string(),
191/// shared_secret: "hfiuhworzwnur98d3wjiwhr".to_string(),
192/// size: Some(100000)
193/// }),
194/// ..Default::default()
195/// };
196/// let json: Value = serde_json::from_str(r#"{
197/// "name": "multi",
198/// "webdav": {
199/// "uri": "7c084226-d9a1-11e6-bf26-cec0c932ce01",
200/// "sharedSecret": "hfiuhworzwnur98d3wjiwhr",
201/// "permissions": ["read", "write"],
202/// "requirements": ["mfa-enforced"]
203/// },
204/// "webapp": {
205/// "uri": "7c084226-d9a1-11e6-bf26-cec0c932ce01",
206/// "sharedSecret": "hfiuhworzwnur98d3wjiwhr",
207/// "viewMode": "read"
208/// },
209/// "datatx": {
210/// "srcUri": "7c084226-d9a1-11e6-bf26-cec0c932ce01",
211/// "sharedSecret": "hfiuhworzwnur98d3wjiwhr",
212/// "size": 100000
213/// }
214/// }"#).unwrap();
215/// assert_eq!(json!(single_protocol_new), json);
216/// ```
217// example:
218// multipleProtocols:
219// name: multi
220// webapp:
221// uri: 7c084226-d9a1-11e6-bf26-cec0c932ce01
222// sharedSecret: hfiuhworzwnur98d3wjiwhr
223// viewMode: read
224// datatx:
225// srcUri: 7c084226-d9a1-11e6-bf26-cec0c932ce01
226// sharedSecret: hfiuhworzwnur98d3wjiwhr
227// size: 100000
228#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
229#[serde(rename_all = "camelCase")]
230pub struct Protocol {
231 /// The name of the protocol. Default: `multi`.
232 /// If `multi` is given, one or more protocol endpoints are expected
233 /// to be defined according to the optional properties specified below.
234 /// Otherwise, at least `webdav` is expected to be supported, and
235 /// its options MAY be given in the opaque `options` payload for
236 /// compatibility with v1.0 implementations (see examples). Note
237 /// though that this format is deprecated.
238 /// Warning: client implementers should be aware that v1.1 servers
239 /// MAY support both `webdav` and `multi`, but v1.0 servers MAY
240 /// only support `webdav`.
241 /// This field may be removed in a future major version of the spec.
242 pub name: String,
243 /// This property is now deprecated. Implementations are
244 /// encouraged to transition to the new optional properties
245 /// defined below, such that this field may be removed in a future major
246 /// version of the spec.
247 #[deprecated]
248 #[serde(skip_serializing_if = "Option::is_none", default)]
249 pub options: Option<HashMap<String, Value>>,
250 #[serde(skip_serializing_if = "Option::is_none", default)]
251 pub webdav: Option<WebDavProperties>,
252 #[serde(skip_serializing_if = "Option::is_none", default)]
253 pub webapp: Option<WebAppProperties>,
254 #[serde(skip_serializing_if = "Option::is_none", default)]
255 pub datatx: Option<DataTxProperties>,
256 /// Any optional additional protocols supported for this resource
257 /// MAY
258 /// be provided here, along with their custom payload. Appropriate
259 /// capabilities MUST be advertised in order for a sender to ensure
260 /// the recipient can parse such customized payloads.
261 #[serde(skip_serializing_if = "HashMap::is_empty", default)]
262 #[serde(flatten)]
263 pub additional_protocols: HashMap<String, Value>,
264}
265
266#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
267#[serde(rename_all = "camelCase")]
268pub struct WebDavProperties {
269 // An URI to access the remote resource. The URI SHOULD be
270 // relative,
271 // such as a key or a UUID, in which case the prefix exposed by the
272 // `/.well-known/ocm` endpoint MUST be used to access the resource,
273 // or it MAY be absolute, including a hostname. The latter is NOT
274 // recommended because of security concerns.
275 // In all cases, for a `folder` resource, the composed URI acts
276 // as the root path, such that other files located within SHOULD
277 // be accessible by appending their relative path to that URI.
278 pub uri: String,
279 // An optional secret to be used to access the resource, such
280 // as
281 // a bearer token. If a `code` is provided, it SHOULD be used
282 // instead via the code flow interaction, and the `sharedSecret`
283 // SHOULD be omitted. To prevent leaking it in logs it MUST NOT
284 // appear in any URI.
285 #[serde(skip_serializing_if = "Option::is_none", default)]
286 pub shared_secret: Option<String>,
287 /// The permissions granted to the sharee.
288 #[serde(skip_serializing_if = "Vec::is_empty", default)]
289 pub permissions: Vec<WebDavPermissions>,
290 /// A list of requirements that the recipient provider MUST
291 /// fulfill
292 /// to access the resource. Requirements are optional, but if it is
293 /// present it MUST NOT be empty. A recipient provider MUST reject
294 /// a share whose requirements it does not understand.
295 #[serde(skip_serializing_if = "Vec::is_empty", default)]
296 pub requirements: Vec<WebDavRequirements>,
297}
298
299#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
300#[serde(rename_all = "kebab-case")]
301pub enum WebDavRequirements {
302 /// `mfa-enforced` requires the user accessing the resource to be
303 /// MFA-authenticated. This requirement MAY be used if the
304 /// recipient provider exposes the `enforce-mfa` capability.
305 MfaEnforced,
306 /// `use-code` requires the recipient to exchange the given
307 /// `code` via a signed HTTPS request to `/token` at the Sending
308 /// Server, in order to get a short-lived token to be used for
309 /// subsequent access. This requirement MAY be used if the
310 /// recipient provider exposes the `receive-code` capability.
311 UseCode,
312}
313
314#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
315#[serde(rename_all = "kebab-case")]
316pub enum WebDavPermissions {
317 /// allows read-only access including download of a copy.
318 Read,
319 /// allows create, update, and delete rights on the resource.
320 Write,
321 /// allows re-share rights on the resource.
322 Share,
323}
324
325impl TryFrom<WebDavProperties> for HashMap<String, Value> {
326 // FIXME use proper error
327 type Error = String;
328
329 fn try_from(value: WebDavProperties) -> Result<Self, Self::Error> {
330 let json = json!(value);
331 serde_json::from_value(json).map_err(|e| e.to_string())
332 }
333}
334
335#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
336#[serde(rename_all = "camelCase")]
337pub struct WebAppProperties {
338 /// An URI to a client-browsable view of the remote resource,
339 /// such that
340 /// users may use a web application available at the sender site.
341 /// The URI SHOULD be relative, such as a key or a UUID, in which case
342 /// the prefix exposed by the `/.well-known/ocm` endpoint MUST be used
343 /// to access the resource, or it MAY be absolute, including a hostname.
344 /// The latter is NOT recommended because of security concerns.
345 /// In all cases, for a `folder` resource, the composed URI acts
346 /// as the root path, such that other files located within SHOULD
347 /// be accessible by appending their relative path to that URI.
348 pub uri: String,
349 /// The permissions granted to the sharee.
350 pub view_mode: WebAppViewMode,
351 /// An optional secret to be used to access the remote web
352 /// app, such as
353 /// a bearer token. To prevent leaking it in logs it MUST NOT appear
354 /// in any URI. If a `code` is provided, then the sending host MUST
355 /// accept the short-lived bearer token when serving the web app,
356 /// which can be exchanged in the code flow interaction. The exchange
357 /// MAY already have happened if the recipient accessed the underlying
358 /// resource via WebDAV, in a multi-protocol scenario. In this case,
359 /// the `sharedSecret` SHOULD be omitted.
360 #[serde(skip_serializing_if = "Option::is_none", default)]
361 pub shared_secret: Option<String>,
362}
363
364#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
365#[serde(rename_all = "kebab-case")]
366pub enum WebAppViewMode {
367 /// `read` allows read-only access including download of a copy.
368 Read,
369 /// `write` allows create, update, and delete rights on the resource.
370 Write,
371 /// `share` allows re-sharing rights on the resource.
372 Share,
373}
374
375#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
376#[serde(rename_all = "camelCase")]
377pub struct DataTxProperties {
378 /// An optional secret to be used to access the resource, such
379 /// as
380 /// a bearer token. If a `code` is provided, it SHOULD be used
381 /// instead via the code flow interaction, and the `sharedSecret`
382 /// SHOULD be omitted. To prevent leaking it in logs it MUST NOT
383 /// appear in any URI.
384 pub shared_secret: String,
385
386 /// An URI to access the resource at the sending server. The
387 /// URI
388 /// SHOULD be relative, such as a key or a UUID, in which case the
389 /// prefix exposed by the `/.well-known/ocm` endpoint SHOULD be used
390 /// to access the resource, or it MAY be absolute, including
391 /// a hostname. The latter is NOT recommended because of security
392 /// concerns.
393 pub src_uri: String,
394 /// The size of the file to be transferred from the sending server.
395 pub size: Option<u64>,
396}
397
398/// Consumer successfully received the share. The response might contain
399/// the display name of the recipient of the share for general user
400/// experience improvement.
401#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
402#[serde(rename_all = "camelCase")]
403pub struct ShareCreationResponse {
404 /// display name of the recipient
405 /// example: John Doe
406 pub recipient_display_name: Option<String>,
407}