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}