From 04ec87b84993e11fbb212a47edccb4156cef315a Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Thu, 5 Sep 2024 10:36:28 +0200 Subject: [PATCH 01/15] Server Discovery --- README.md | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4754efd..25e8b81 100644 --- a/README.md +++ b/README.md @@ -127,19 +127,39 @@ Following these step, both servers MAY display the `name` of the other party as For further details on this concept, see also [#54](https://github.com/cs3org/OCM-API/pull/54) and related issues. For a discussion about trust policies, see [sciencemesh#196](https://github.com/sciencemesh/sciencemesh/issues/196). -### Discovery -Authentication between services is already established. This means that this specification doesn't cover the way a service authenticates incoming API calls (e.g. through an API Key, VPN connection or IP whitelisting). In this scope we assume that the services are already authenticated. +### OCM API Discovery +After establishing contact as discussed in the previous section, the Sharing User can send the Share Creation Gesture to the Sending Server, providing the Sending Server with the following information: +* Resource to be shared +* Protocol to be offered for access +* Sending Party's identifier +* Receiving Party's identifier +* Receiving Server FQDN +* OPTIONAL: Name +* OPTIONAL: Permissions -If a finite whitelist of receiver servers exists on the sender side, then this list may already contain all necessary endpoint details. +The next step is for the Sending Server to additionally discover: +* if the Receiving Server is trusted +* if the Receiving Server supports OCM +* if so, which version and with which optional functionality +* at which URL +* the public key the Receiving Server will use for HTTP Signatures (if any) -When a sending server allows sharing to any internet-hosted receiving server, then discovery can happen from the sharee address, using the `/.well-known/ocm` (or `/ocm-provider`, for backwards compatibility) URL that receiving servers SHOULD provide according to this [specification](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1.well-known~1ocm/get). +The Sending Server MAY first preform denylist and allowlist checks on the FQDN. -To ease the process of confirming the identity of a remote party, the discovery data MAY contain a public key: each incoming request that requires to origin from an authenticated source MUST be signed in its headers using the private key of that source, whose public key MUST be exposed in its discovery data. +If a finite allowlist of receiver servers exists on the sender side, then this list may already contain all necessary information. -To fill the gap between users knowning other peers' email addresses of the form `user@provider.org`, and the actual cloud storage endpoints being in the form `https://my-cloud-storage.provider.org`, a further discovery mechanism MAY be provided in case hosting https://provider.org/.well-known/ocm is impractical, based on DNS `SRV` Service Records. +If the FQDN passes the denylist and/or allowlist checks, but no details about its OCM API are known, the Sending Server can use the following process to try to fetch this information from the Receiving Server. -* If e.g. https://provider.org/.well-known/ocm does not exist, a provider MAY instead point to e.g. https://my-cloud-storage.provider.org/.well-known/ocm by ensuring that a `type=SRV` DNS query to `_ocm._tcp.provider.org` resolves to e.g. `service = 10 10 443 my-cloud-storage.provider.org` -* When requested to discover the EFSS endpoint for `user@provider.org`, if https://provider.org/.well-known/ocm can not be fetched, implementations SHOULD fall back to querying the corresponding `_ocm._tcp.domain` DNS record, e.g. `_ocm._tcp.provider.org`, and subsequently make a HTTP GET request to the host returned by that DNS query, followed by the `/.well-known/ocm` URL path. +This process MAY be influenced by a VPN connection and/or IP allowlisting. + +Otherwise, for instance +when a sending server allows sharing to any internet-hosted receiving server, then discovery can happen from the Receiving Server FQDN, using `https:///.well-known/ocm` (or `https:///ocm-provider`, for backwards compatibility) as the URL. The Receiving Server SHOULD provide both of these. See the [API specification](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1.well-known~1ocm/get) for a normative definition of both endpoints. + +To help with situations where hosting `https:///.well-known/ocm` or `https:///ocm-provider` is impractical, a further discovery mechanism MAY be provided, based on DNS `SRV` Service Records. + +If `https:///.well-known/ocm` does not exist, the Receiving Server MAY instead point to `https:///.well-known/ocm` by ensuring that a `type=SRV` DNS query to `_ocm._tcp.` resolves to e.g. `service = 10 10 443 ` + +When attempting to discover the OCM API details for ``, if https:///.well-known/ocm can not be fetched, implementations SHOULD fall back to querying the corresponding `_ocm._tcp.` DNS record, e.g. `_ocm._tcp.provider.org`, and subsequently make a HTTP GET request to the host returned by that DNS query, followed by the `/.well-known/ocm` URL path, using TLS. ### Share Creation To create a share, the sending server SHOULD make a HTTP POST request to the `/shares` endpoint of the receiving server ([docs](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1shares/post)). From 8a7814c57f7779accce0ba90ebb03c181833b1ad Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Thu, 5 Sep 2024 10:37:18 +0200 Subject: [PATCH 02/15] Share Creation Notification --- README.md | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 25e8b81..48f9f7f 100644 --- a/README.md +++ b/README.md @@ -161,11 +161,32 @@ If `https:///.well-known/ocm` does not exist, the Receiving Server MAY ins When attempting to discover the OCM API details for ``, if https:///.well-known/ocm can not be fetched, implementations SHOULD fall back to querying the corresponding `_ocm._tcp.` DNS record, e.g. `_ocm._tcp.provider.org`, and subsequently make a HTTP GET request to the host returned by that DNS query, followed by the `/.well-known/ocm` URL path, using TLS. -### Share Creation -To create a share, the sending server SHOULD make a HTTP POST request to the `/shares` endpoint of the receiving server ([docs](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1shares/post)). +### Share Creation Notification +To create a share, the sending server SHOULD make a HTTP POST request +* to the `/shares` path in the Invite Sender OCM Server's OCM API +* using `application/json` as the `Content-Type` HTTP request header +* its request body containing a JSON document representing an object with the fields as described in the ([API docs](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1shares/post)) +* using TLS +* using [httpsig](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12) + +The Receiving Server MAY discard the notification if any of the following hold true: +* the HTTP Signature is missing +* the HTTP Signature is not valid +* no keypair is trusted or discoverable from the FQDN part of the `sender` field in the request body +* the keypair used to generate the HTTP Signature doesn't match the one trusted or discoverable from the FQDN part of the `sender` field in the request body +* the Sending Server is denylisted +* the Sending Server is not allowlisted +* the Sending Party is not trusted by the Receiving Party (i.e. the Sending Party's OCM Address does not appear in the Receiving Party's addressbook) +* the Receiving Server is unable to act as an API client for (any of) the protocol(s) listed for accessing the resource +* an initial check shows that the resource cannot successfully accessed through (any of) the protocol(s) listed + +### Receiving Party Notification +If the Share Creation Notification is not discarded by the Receiving Server, they MAY notify the Receiving Party passively by adding the Share to some inbox list, and MAY also notify them actively through for instance a push notification or an email message. + +They could give the Receiving Party the option to accept or reject the share, or add the share automatically and only send an informational notification that this happened. -### Share Acceptance -In response to a share creation, the receiving server MAY send back a [notification](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1notifications/post) to the sending server, with `notificationType` set to `"SHARE_ACCEPTED"` or `"SHARE_DECLINED"`. The sending server MAY expose this information to the end user. +### Share Acceptance Notification +In response to a Share Creation Notification, the Receiving Server MAY send back a [notification](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1notifications/post) to the Sending Server, with `notificationType` set to `"SHARE_ACCEPTED"` or `"SHARE_DECLINED"`. The Sending Server MAY expose this information to the Sending Party. ### Share Access To access a share, the receiving server MAY use multiple ways, depending on the received payload and on the `protocol.name` property: From 6348ed58e9b92c81e9eef8da54d8cfa27ba53326 Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Thu, 5 Sep 2024 10:37:54 +0200 Subject: [PATCH 03/15] Accessing the Resource --- README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 48f9f7f..ad53eca 100644 --- a/README.md +++ b/README.md @@ -188,17 +188,18 @@ They could give the Receiving Party the option to accept or reject the share, or ### Share Acceptance Notification In response to a Share Creation Notification, the Receiving Server MAY send back a [notification](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1notifications/post) to the Sending Server, with `notificationType` set to `"SHARE_ACCEPTED"` or `"SHARE_DECLINED"`. The Sending Server MAY expose this information to the Sending Party. -### Share Access -To access a share, the receiving server MAY use multiple ways, depending on the received payload and on the `protocol.name` property: +### Resource Access +To access the Resource, the Receiving Server MAY use multiple ways, depending on the body of the Share Creation Notification and on the `protocol.name` property in there: -* If `protocol.name` = `multi`, the receiver MUST make a HTTP PROPFIND request to `protocol.webdav.uri` to access the remote share. If `protocol.webdav.sharedSecret` is not empty, the receiver MUST pass it as a `Authorization: bearer` header. -Otherwise, if `protocol.webdav.code` is not empty, the receiver SHOULD discover the sender's OCM endpoint and make a signed POST request to `/token`, to exchange +* If `protocol.name` = `multi`, the receiver MUST make a HTTP PROPFIND request to `protocol.webdav.uri` to access the remote share. +If `code` is not empty, the receiver SHOULD discover the sender's OCM endpoint and make a signed POST request to the `/token` path inside the Sending Server's OCM API, to exchange the code for a short-lived bearer token, -and then use that bearer token to access the remote share. +and then use that bearer token to access the Resource. +Otherwise, if `protocol.webdav.sharedSecret` is not empty, the receiver MUST pass it as a `Authorization: bearer` header. -* If `protocol.name` = `webdav`, the receiver SHOULD inspect the `protocol.options` property. If it contains a `sharedSecret`, as in the [legacy example](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1shares/post), then the receiver SHOULD make a HTTP PROPFIND request to `https://:@`, where `` is the remote server, and `` is obtained by querying the [Discovery](#discovery) endpoint at the remote server and getting `resourceTypes[0].protocols.webdav`. Note that this access method is _deprecated_ and may be removed in a future release of the Protocol. +* If `protocol.name` = `webdav`, the receiver SHOULD inspect the `protocol.options` property. If it contains a `sharedSecret`, as in the [legacy example](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1shares/post), then the receiver SHOULD make a HTTP PROPFIND request to `https://:@`, where `` is the remote server, and `` is obtained by querying the [Discovery](#discovery) endpoint at the Sending Server and getting `resourceTypes[0].protocols.webdav`. Note that this access method is _deprecated_ and may be removed in a future release of the Protocol. -In both cases, when the share is a folder and the receiver accesses a resource within the share, it SHOULD append its relative path to that URL. +In both cases, when the Resource is a folder and the Receiving Server accesses a resource within that shared folder, it SHOULD append its relative path to that URL. Additionally, if `protocol..permissions` include `mfa-enforced`, the receiving host MUST ensure that the user accessing the resource has been authenticated with MFA. @@ -215,13 +216,13 @@ receiving server to persuarde the sending server to share the same resource with TODO: document how receiver.com can know if sender.com understood and processed the reshare request. -### Multi Factor Authentication -If an OCM provider exposes the capability `/mfa-capable`, it indicates that it will try and comply with a MFA requirement set as a permission on a share. If the sharer OCM provider trusts the receiver OCM provider, the sharer MAY set the permission `mfa-enforced` on a share, which SHOULD be honored. A compliant OCM provider that signals that it is MFA-capable MUST not allow access to a resource protected with the `mfa-enforced` permission, if the consumer has not provided a second factor to establish their identity with greater confidence. +## Appendix A: Multi Factor Authentication +If a Receiving Server exposes the capability `/mfa-capable`, it indicates that it will try and comply with a MFA requirement set as a permission on a Share. If the Sending Server trusts the Receiving Server, the Sending Server MAY set the permission `mfa-enforced` on a Share, which the Receiving Server SHOULD honor. A compliant Receiving Server that signals that it is MFA-capable MUST not allow access to a resource protected with the `mfa-enforced` permission, if the Receiving Party has not provided a second factor to establish their identity with greater confidence. -Since there is no way to guarantee that the sharee OCM provider will actually enforce the MFA requirement, it is up to the sharer OCM provider to establish a trust with the OCM sharee provider such that it is reasonable to assume that the sharee OCM provider will honor the MFA requirement. This establishment of trust will inevitably be implementation dependent, and can be done for example using a pre approved allow list of trusted OCM providers. The procedure of establishing trust is out of scope for this specification: a mechanism similar to the [ScienceMesh](https://sciencemesh.io) integration for the [Invite](#invite) capability may be envisaged. +Since there is no way to guarantee that the Receiving Server will actually enforce the MFA requirement, it is up to the Sending Server to establish a trust with the Receiving Server such that it is reasonable to assume that the Receiving Server will honor the MFA requirement. This establishment of trust will inevitably be implementation dependent, and can be done for example using a pre approved allow list of trusted Receiving Servers. The procedure of establishing trust is out of scope for this specification: a mechanism similar to the [ScienceMesh](https://sciencemesh.io) integration for the [Invite](#invite) capability may be envisaged. -## Signing request +## Appendix B: Request Signing A request is signed by adding the signature in the headers. The sender also needs to expose the public key used to generate the signature. The receiver can then validate the signature and therefore the origin of the request. To help debugging, it is recommended to also add all properties used in the signature as headers, even if they can easily be re-generated from the payload. @@ -269,7 +270,7 @@ This is a quick PHP example of headers for outgoing request: ]; openssl_sign(implode("\n", $headers), $signed, $privateKey, OPENSSL_ALGO_SHA256); - + $signature = [ 'keyId' => 'https://author.hostname/key', 'algorithm' => 'rsa-sha256', From 643fdbd612983b09e8bb61ab8f1aa3124077fd08 Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Fri, 6 Sep 2024 11:21:38 +0200 Subject: [PATCH 04/15] Add code / token flow in spec.yaml --- spec.yaml | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/spec.yaml b/spec.yaml index d576955..f1f9a53 100644 --- a/spec.yaml +++ b/spec.yaml @@ -236,6 +236,27 @@ paths: description: Invitation already accepted. schema: $ref: "#/definitions/Error" + /token: + post: + summary: Obtain a (potentially short-lived) bearer token in exchange for a code + description: > + See https://github.com/cs3org/OCM-API?tab=readme-ov-file#share-access + parameters: + - name: token-request + in: body + description: The JSON request body. + required: true + schema: + $ref: "#/definitions/TokenRequest" + responses: + 200: + description: Token issued. + schema: + $ref: "#/definitions/TokenResponse" + 403: + description: Token denied. + schema: + $ref: "#/definitions/Error" definitions: "400": @@ -460,6 +481,10 @@ definitions: The expiration time for the OCM share, in seconds of UTC time since Unix epoch. If omitted, it is assumed that the share does not expire. + code: + type: string + description: | + A nonce to be exchanged for a (potentially short-lived) bearer token at the Sending Server's /token endpoint. protocol: type: object description: | @@ -684,3 +709,40 @@ definitions: type: string description: Name of the user that sent the invite. example: John Doe + TokenRequest: + type: object + allOf: + - properties: + client_id: + type: string + format: fqdn + description: FQDN of the Receiving Server. + example: receiver.org + code: + type: string + description: Code received in the Share Creation Notification + example: xyz + grant_type: + type: string + description: Must be set to 'ocm_authorization_code' + example: ocm_authorization_code + TokenResponse: + type: object + allOf: + - properties: + access_token: + type: string + description: The bearer token to be used to access the protocol-specific API(s) + example: asdfgh + token_type: + type: string + description: Must be set to 'bearer' + example: bearer + expires_in: + type: number + description: Number of seconds before this access_token will need to be refreshed. + example: 3600 + refresh_token: + type: string + description: A refresh token + example: qwertyuiop From 416071fed8f706a147c19399549dab9edc455a60 Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Thu, 5 Sep 2024 10:39:11 +0200 Subject: [PATCH 05/15] Consolidate scope and assumptions section --- README.md | 56 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index e96a936..a0bb2c9 100644 --- a/README.md +++ b/README.md @@ -2,36 +2,46 @@ ![Open Cloud Mesh Protocol Specification](logo.png) -This repository contains the specification of the Open Cloud Mesh protocol, including -the [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (fka Swagger) specification for its API. This specification describes disovery and use of the RESTful API endpoints, request and response headers, possible response codes, request and response formats, hypermedia controls, error handling, and other API design best practices which vendors should support to make sharing of resources between different vendors possible. +This repository contains the text of the Open Cloud Mesh Internet-Draft, as well as +the equivalent [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (fka Swagger) specification for its API. -* [Scope and assumptions](#scope-and-assumptions) * [Specification](#specification) - * [Discovery](#discovery) - * [Share Creation](#create) - * [Share Acceptance](#accept) - * [Share Access](#access) - * [Share Deletion](#unshare) - * [Share Updating](#update) - * [Resharing](#reshare) - * [Invite](#invite) - * [Signing Request](#signing-request) - + * [Introduction](#introduction) + * [Terms](#terms) + * [Establishing Contact](#establishing-contact) + * [Direct Entry](#direct-entry) + * [Address books](#address-books) + * [Public Link Flow](#public-link-flow) + * [Public Invite Flow](#public-invite-flow) + * [Invite Flow](#invite-flow) + * [Rationale](#rationale) + * [Steps](#steps) + * [Invite Acceptance Request Details](#invite-acceptance-request-details) + * [Invite Acceptance Response Details](#invite-acceptance-response-details) + * [Further Reading](#further-reading) + * [OCM API Discovery](#ocm-api-discovery) + * [Share Creation Notification](#share-creation-notification) + * [Receiving Party Notification](#receiving-party-notification) + * [Share Acceptance Notification](#share-acceptance-notification) + * [Resource Access](#resource-access) + * [Share Deletion](#share-deletion) + * [Share Updating](#share-updating) + * [Resharing](#resharing) +* [Appendix A: Multi Factor Authentication](#appendix-a-multi-factor-authentication) +* [Appendix B: Request Signing](#appendix-b-request-signing) + * [How to generate the Signature for outgoing request](#how-to-generate-the-signature-for-outgoing-request) + * [How to confirm Signature on incoming request](#how-to-confirm-signature-on-incoming-request) + * [Validating the payload](#validating-the-payload) +* [Changelog](#changelog) * [Contributing](#contributing) -## Scope and assumptions - -* For the core sharing functionality, the provider knows the consumer (both endpoint and user) when it creates a share with the consumer (also see [#26](https://github.com/cs3org/OCM-API/issues/26)). In addition, an optional invitation workflow is available in this specification (see below), which gives the consumer a way to automatically trust a provider (and vice versa). The [ScienceMesh](https://sciencemesh.io) infrastructure provides a managed white list of trusted federated sites. -* Consumer doesn't have to accept a share, the resource will be available to the consumer immediately ([#25](https://github.com/cs3org/OCM-API/issues/25)). -* Dealing with incoming shares is a vendor specific implementation. One vendor might use an 'accept before' process while another vendor might use a 'decline after' approach. This is considered part of the UX and thus not part of the interaction between different vendors. However, the consumer could notify the provider by using the introduced `/notifications` endpoint (also see [#27](https://github.com/cs3org/OCM-API/issues/27)). -* Reverting access to outgoing shares is a vendor specific implementation. One vendor might delete an entire share while another might invalidate an access token. This is considered part of vendor-specific internals and thus not part of the interaction between different vendors. However, the provider could notify the consumer by using the introduced `/notifications` endpoint (also see [#27](https://github.com/cs3org/OCM-API/issues/27)). -* The actual file sync is not part of this specification. To keep this specification 'future proof', the file sync protocol will be embedded as a separate object in Open Cloud Mesh API calls. This protocol object contains all protocol specific options, e.g. WebDAV specific options. - ## Specification ### Introduction Open Cloud Mesh is a server federation protocol that is used to notify a remote user that they have been granted access to some resource. It has similarities with authorization flows such as OAuth, as well as with social internet protocols such as ActivityPub and email. +The actual file sync is not part of this specification. To keep this specification 'future proof', the file sync protocol will be embedded as a separate object in Open Cloud Mesh API calls. This protocol object contains all protocol specific options, e.g. WebDAV specific options. + ### Terms We define the following concepts (with some non-normative references to related concepts from OAuth and elsewhere): * __Resource__ - the piece of data or interaction to which access is being granted @@ -155,7 +165,7 @@ If the FQDN passes the denylist and/or allowlist checks, but no details about it This process MAY be influenced by a VPN connection and/or IP allowlisting. Otherwise, for instance -when a sending server allows sharing to any internet-hosted receiving server, then discovery can happen from the Receiving Server FQDN, using `https:///.well-known/ocm` (or `https:///ocm-provider`, for backwards compatibility) as the URL. The Receiving Server SHOULD provide both of these. See the [API specification](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1.well-known~1ocm/get) for a normative definition of both endpoints. +when a sending server allows sharing to any internet-hosted receiving server, then trust can be established dynamically, and OCM API discovery can happen from the Receiving Server FQDN, using `https:///.well-known/ocm` (or `https:///ocm-provider`, for backwards compatibility) as the URL. The Receiving Server SHOULD provide both of these. See the [API specification](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1.well-known~1ocm/get) for a normative definition of both endpoints. To help with situations where hosting `https:///.well-known/ocm` or `https:///ocm-provider` is impractical, a further discovery mechanism MAY be provided, based on DNS `SRV` Service Records. @@ -240,6 +250,8 @@ Additionally, if `protocol..permissions` include `mfa-enforced`, t A `"SHARE_ACCEPTED"` notification followed by a `"SHARE_UNSHARED"` notification is equivalent to a `"SHARE_DECLINED"` notification. +Reverting access to outgoing shares is a vendor specific implementation. One vendor might delete an entire share while another might invalidate an access token. This is considered part of vendor-specific internals and thus not part of the interaction between different vendors. However, the provider could notify the consumer by using the introduced `/notifications` endpoint (also see [#27](https://github.com/cs3org/OCM-API/issues/27)). + ### Share Updating TODO: document `"RESHARE_CHANGE_PERMISSION"` From 898906f60b126278bcf434d773180f7e6f6057d1 Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Thu, 5 Sep 2024 10:58:10 +0200 Subject: [PATCH 06/15] capitalise defined terms, other small corrections --- README.md | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index a0bb2c9..fae9761 100644 --- a/README.md +++ b/README.md @@ -37,30 +37,30 @@ the equivalent [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (fka Swag ## Specification ### Introduction -Open Cloud Mesh is a server federation protocol that is used to notify a remote user that they have -been granted access to some resource. It has similarities with authorization flows such as OAuth, as well as with social internet protocols such as ActivityPub and email. +Open Cloud Mesh is a server federation protocol that is used to notify a Receiving Party that they have +been granted access to some Resource. It has similarities with authorization flows such as OAuth, as well as with social internet protocols such as ActivityPub and email. -The actual file sync is not part of this specification. To keep this specification 'future proof', the file sync protocol will be embedded as a separate object in Open Cloud Mesh API calls. This protocol object contains all protocol specific options, e.g. WebDAV specific options. +Open Cloud Mesh only handles the necessary interactions up to the point where the Receiving Party is informed that they were granted access to the Resource. The actual resource access is then left to protocols such as WebDAV and others. ### Terms We define the following concepts (with some non-normative references to related concepts from OAuth and elsewhere): -* __Resource__ - the piece of data or interaction to which access is being granted -* __Share__ - a policy rule stating that certain actors are allowed access to a resource. Also: a record in a database representing this rule -* __Sending Party__ - a person or party who is authorized to create shares ("Resource Owner") -* __Receiving Party__ - a person, group or party who is granted access to the resource through the share ("Requesting Party / RqP" in OAuth-UMA) +* __Resource__ - the piece of data or interaction to which access is being granted, e.g. a file, folder, video call, or printer queue +* __Share__ - a policy rule stating that certain actors are allowed access to a Resource. Also: a record in a database representing this rule +* __Sending Party__ - a person or party who is authorized to create Shares (similar to "Resource Owner" in OAuth) +* __Receiving Party__ - a person, group or party who is granted access to the Resource through the Share (similar to "Requesting Party / RqP" in OAuth-UMA) * __Sending Server__ - the server that: - * holds the resource ("file server", "Entreprise File Sync and Share (EFSS) server"), - * provides access to it ("API"), - * takes the decision to create the share based on user interface gestures from the sending user ("Authorization Server") - * takes the decision about authorizing attempts to access the resource ("Resource Server") - * send out share creation notifications when appropriate (see below) + * holds the Resource ("file server" or "Entreprise File Sync and Share (EFSS) server" role), + * provides access to it (by exposing at least one "API"), + * takes the decision to create the Share based on user interface gestures from the Sending Party (the "Authorization Server" role in OAuth) + * takes the decision about authorizing attempts to access the Resource (the "Resource Server" role in OAuth) + * send out Share Creation Notifications when appropriate (see below) * __Receiving Server__ - the server that: - * receives share creation notifications (see below) - * actively or passively notifies the receiving user or group of any incoming share creation notification - * acts as an API client, allowing the receiving user to access the resource through an API (e.g. WebDAV) of the sending server -* __Sending Gesture__ - a user interface interaction from the Sending Party to the Sending Server, conveying the intention to create a share + * receives Share Creation Notifications (see below) + * actively or passively notifies the receiving user or group of any incoming Share Creation Notification + * acts as an API client, allowing the receiving user to access the Resource through an API (e.g. WebDAV) of the sending server +* __Sending Gesture__ - a user interface interaction from the Sending Party to the Sending Server, conveying the intention to create a Share * __Share Creation__ - the addition of a Share to the database state of the Sending Server, in response to a successful Sending Gesture or for another reason -* __Share Creation Notification__ - a server-to-server request from the sending server to the receiving server, notifying the receiving server that a share has been created +* __Share Creation Notification__ - a server-to-server request from the sending server to the receiving server, notifying the receiving server that a Share has been created * __OCM Address__ - a string of the form `@` which can be used to uniquely identify a user or group "at" an OCM-capable server. `` is an opaque string, unique at the server. `` is the Fully Qualified Domain Name by which the server is identified. This can, but doesn't need to be, the domain at which the OCM API of that server is hosted. * __OCM Notification__ - a message from the Receiving Server to the Sending Server or vice versa, using the OCM Notifications endpoint. @@ -89,7 +89,7 @@ The Sending Server MAY offer the Sending Party an address book tool, where OCM A An interface for anonymously viewing a Resource on the Sending Server, MAY allow any internet user to type or paste an OCM address into an HTML form, as a Sending Gesture. This means that the Sending Party and the Receiving Party could be the same person, so contact between them does not need to be explicitly established. #### Public Invite Flow -Similarly, an interface on the Sending Server, MAY allow any internet user to type or paste an OCM address into an HTML form, as a Sending Gesture for a given Resource, without itself providing a way to access that particular Resource. A link to this interface could then for instance be shared on a mailing list, allowing all subscribers to effectively request access to the resource by making a Sending Gesture to the Sending Server with their own OCM Address. +Similarly, an interface on the Sending Server, MAY allow any internet user to type or paste an OCM address into an HTML form, as a Sending Gesture for a given Resource, without itself providing a way to access that particular Resource. A link to this interface could then for instance be shared on a mailing list, allowing all subscribers to effectively request access to the Resource by making a Sending Gesture to the Sending Server with their own OCM Address. #### Invite Flow ##### Rationale @@ -220,13 +220,13 @@ The Receiving Server MAY discard the notification if any of the following hold t * the Sending Server is denylisted * the Sending Server is not allowlisted * the Sending Party is not trusted by the Receiving Party (i.e. the Sending Party's OCM Address does not appear in the Receiving Party's addressbook) -* the Receiving Server is unable to act as an API client for (any of) the protocol(s) listed for accessing the resource -* an initial check shows that the resource cannot successfully accessed through (any of) the protocol(s) listed +* the Receiving Server is unable to act as an API client for (any of) the protocol(s) listed for accessing the Resource +* an initial check shows that the Resource cannot successfully accessed through (any of) the protocol(s) listed ### Receiving Party Notification If the Share Creation Notification is not discarded by the Receiving Server, they MAY notify the Receiving Party passively by adding the Share to some inbox list, and MAY also notify them actively through for instance a push notification or an email message. -They could give the Receiving Party the option to accept or reject the share, or add the share automatically and only send an informational notification that this happened. +They could give the Receiving Party the option to accept or reject the Share, or add the Share automatically and only send an informational notification that this happened. ### Share Acceptance Notification In response to a Share Creation Notification, the Receiving Server MAY send back a [notification](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1notifications/post) to the Sending Server, with `notificationType` set to `"SHARE_ACCEPTED"` or `"SHARE_DECLINED"`. The Sending Server MAY expose this information to the Sending Party. @@ -244,21 +244,21 @@ Otherwise, if `protocol.webdav.sharedSecret` is not empty, the receiver MUST pas In both cases, when the Resource is a folder and the Receiving Server accesses a resource within that shared folder, it SHOULD append its relative path to that URL. -Additionally, if `protocol..permissions` include `mfa-enforced`, the receiving host MUST ensure that the user accessing the resource has been authenticated with MFA. +Additionally, if `protocol..permissions` include `mfa-enforced`, the Receiving Server MUST ensure that the Receiving Party has been authenticated with MFA. ### Share Deletion A `"SHARE_ACCEPTED"` notification followed by a `"SHARE_UNSHARED"` notification is equivalent to a `"SHARE_DECLINED"` notification. -Reverting access to outgoing shares is a vendor specific implementation. One vendor might delete an entire share while another might invalidate an access token. This is considered part of vendor-specific internals and thus not part of the interaction between different vendors. However, the provider could notify the consumer by using the introduced `/notifications` endpoint (also see [#27](https://github.com/cs3org/OCM-API/issues/27)). +Reverting access to outgoing shares is a vendor specific implementation. One vendor might delete an entire Share while another might invalidate an access token. This is considered part of vendor-specific internals and thus not part of the interaction between different vendors. However, the provider could notify the consumer by using the introduced `/notifications` endpoint (also see [#27](https://github.com/cs3org/OCM-API/issues/27)). ### Share Updating TODO: document `"RESHARE_CHANGE_PERMISSION"` ### Resharing The `"REQUEST_RESHARE"` and `"RESHARE_UNDO"` notification types MAY be used by the -receiving server to persuarde the sending server to share the same resource with another share recipient. -TODO: document how receiver.com can know if sender.com understood and processed the +Receiving Server to persuade the Sending Server to share the same Resource with another Receiving Party. +TODO: document how the Receiving Party can know if the Sending Party understood and processed the reshare request. ## Appendix A: Multi Factor Authentication From 3ac53672d08ec681e2292b9dd7a3bc8a9fddbba1 Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Thu, 5 Sep 2024 11:56:36 +0200 Subject: [PATCH 07/15] Write out security advantages of the invite flow --- README.md | 45 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index fae9761..9be1027 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ the equivalent [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (fka Swag * [Specification](#specification) * [Introduction](#introduction) * [Terms](#terms) + * [General Flow](#general-flow) * [Establishing Contact](#establishing-contact) * [Direct Entry](#direct-entry) * [Address books](#address-books) @@ -42,6 +43,11 @@ been granted access to some Resource. It has similarities with authorization flo Open Cloud Mesh only handles the necessary interactions up to the point where the Receiving Party is informed that they were granted access to the Resource. The actual resource access is then left to protocols such as WebDAV and others. +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL +NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in +RFC 2119. + ### Terms We define the following concepts (with some non-normative references to related concepts from OAuth and elsewhere): * __Resource__ - the piece of data or interaction to which access is being granted, e.g. a file, folder, video call, or printer queue @@ -75,9 +81,21 @@ unique at the server. `` is the Fully Qualified Domain Name by which the s * __Invite Acceptance Request__ - API call from the Invite Receiver OCM Server to the Invite Sender OCM Server, supplying the Invite Token as well as the OCM Address of the Invite Receiver, effectively allowlisting the Invite Sender OCM Server for sending Share Creation Notifications to the Invite Receiver OCM Server. * __Invite Acceptance Response__ - HTTP response to the Invite Acceptance Request +### General Flow +The lifecycle of an Open Cloud Mesh Share starts with prerequisites such as +establishing trust, establishing contact, and OCM API discovery. + +Then the share creation involves the Sending Party making a Sending Gesture to the Sending Server, +the Sending Server carrying out the actual Share Creation, +and the Sending Server sending a Share Creation Notification to the Receiving Server. + +After this, the Receiving Server MAY notify the Receiving Party and/or the Sending Server, and will act as an API client +through which the Receiving Party can access the Resource. After that, the Share may be updated, deleted, and/or reshared. + ### Establishing Contact -Before the Sending Server can send a Share Creation Notification to the Receiving Server, it needs to establish the Receiving Server's FQDN, and the Receiving Party's identifier, among other things. -Some steps may proceed the Sending Gesture, allowing the Sending Party to establish (with some level of trust) the OCM Address of the Receiving Party. +Before the Sending Server can send a Share Creation Notification to the Receiving Server, it needs to establish the Receiving Party's OCM Address (containing the Receiving Server's FQDN, and the Receiving Party's identifier), among other things. +Some steps may preceed the Sending Gesture, allowing the Sending Party to establish (with some level of trust) the OCM Address of the Receiving Party. In other cases, establishing the OCM Address +of the Receiving Party happens as part of the Sending Gesture. #### Direct Entry The simplest way for this is if the Receiving Party shares their OCM Address with the Sending Party through some out-of-band means, and the Sending Party enters this string into the user interface of the Sending Server, by means of typing or pasting into an HTML form, or clicking a link to a URL that includes the string in some form. @@ -86,10 +104,10 @@ The simplest way for this is if the Receiving Party shares their OCM Address wit The Sending Server MAY offer the Sending Party an address book tool, where OCM Addresses can be stored over time in a labeled and/or searchable way. This decouples the act by which the OCM Address string is passed into the Sending Server's database from the selection of the Receiving Party in preparation for Share Creation. #### Public Link Flow -An interface for anonymously viewing a Resource on the Sending Server, MAY allow any internet user to type or paste an OCM address into an HTML form, as a Sending Gesture. This means that the Sending Party and the Receiving Party could be the same person, so contact between them does not need to be explicitly established. +An interface for anonymously viewing a Resource on the Sending Server MAY allow any internet user to type or paste an OCM address into an HTML form, as a Sending Gesture. This means that the Sending Party and the Receiving Party could be the same person, so contact between them does not need to be explicitly established. #### Public Invite Flow -Similarly, an interface on the Sending Server, MAY allow any internet user to type or paste an OCM address into an HTML form, as a Sending Gesture for a given Resource, without itself providing a way to access that particular Resource. A link to this interface could then for instance be shared on a mailing list, allowing all subscribers to effectively request access to the Resource by making a Sending Gesture to the Sending Server with their own OCM Address. +Similarly, an interface on the Sending Server MAY allow any internet user to type or paste an OCM address into an HTML form, as a Sending Gesture for a given Resource, without itself providing a way to access that particular Resource. A link to this interface could then for instance be shared on a mailing list, allowing all subscribers to effectively request access to the Resource by making a Sending Gesture to the Sending Server with their own OCM Address. #### Invite Flow ##### Rationale @@ -117,8 +135,6 @@ Whereas the precise syntax of the Invite Message and the Invite Acceptance Gestu * using TLS * using [httpsig](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12) -See [Invite Acceptance Request API definition](https://cs3org.github.io/OCM-API/docs.html?branch=master&repo=OCM-API&user=cs3org#/paths/~1invite-accepted/post) for further non-normative documentation. - The Invite Receiver OCM Server SHOULD apply its own policies for trusting the Invite Sender OCM Server before making the Invite Acceptance Request. ##### Invite Acceptance Response Details @@ -130,14 +146,21 @@ The Invite Acceptance Response SHOULD be a HTTP response: * `email` - non-normative / informational; an email address for the Invite Sender. Not necessarily at the same FQDN as their OCM Server * `name` - human-readable name of the Invite Sender, as a suggestion for display in the Invite Receiver's address book -See [Invite Acceptance Request API definition](https://cs3org.github.io/OCM-API/docs.html?branch=master&repo=OCM-API&user=cs3org#/paths/~1invite-accepted/post) for further non-normative documentation. - The Invite Sender OCM Server SHOULD verify the HTTP Signature on the Invite Acceptance Request and apply its own policies for trusting the Invite Receiver OCM Server before processing the Invite Acceptance Request and sending the Invite Acceptance Response. -##### Further Reading -Following these step, both servers MAY display the `name` of the other party as a trusted or white-listed contact, and enable selecting them as a Receiving Party. OCM Servers MAY enforce a policy to only accept Share Creation Notifications from such trusted contacts, or MAY display a warning to users when a Share Creation Notification from an unknown party is received. +##### Addition into address books +Following these step, both servers MAY display the `name` of the other party as a trusted or allowlisted contact, and enable selecting them as a Receiving Party. OCM Servers MAY enforce a policy to only accept Share Creation Notifications from such trusted contacts, or MAY display a warning to users when a Share Creation Notification from an unknown party is received. + +Both servers MAY also allowlist each other as a server with which at least one of their users wishes to interact. + +##### Security Advantages +It is important to underscore the value of the Invite in this scenario, as it provides four important security advantages. First of all, if the Receiving Server blocks Share Creation Notifications from Sending Parties who are not in the addressbook of the Receiving Party, then this protects the Receiving Party from receiving unsolicited Shares. An attacker could still send the Receiving Party an unsolicited Share, but they would first need to convince the Receiving Party through an out-of-band communication channel to accept their invite. In many use cases, the Receiving Party has had other forms of contact with the Sending Party (e.g. in-person or email back-and-forth). The out-of-band Invite Message thus leverages the filters and context which the Receiving Party may already benefit from in that out-of-band communication. For instance, a careful Receiving Party may choose to only accept Invites that reach them via a private or moderated messaging platform. + +Second, when the Receiving Party accepts the Invite, the Receiving Server knows that the Sending Server they are about to interact with is trusted by the Sending Party, which in turn is trusted by the Receiving Party, which in turn is trusted by them. In other words, one of their users is requesting the allowlisting of a server they wish to interact with, in order to interact with a party they know out-of-band. This gives the Receiving Server reason to put more trust in the Sending Server than it would put into an arbitrary internet-hosted server. + +Third, equivalently, the Sending Server knows it is essentially registering the Receiving Server as an API client at the request of the Receiving Party, to whom the right to request this has been traceably delegated by the Sending Party, which is one of its registered users. -For further details on this concept, see also [#54](https://github.com/cs3org/OCM-API/pull/54) and related issues. For a discussion about trust policies, see [sciencemesh#196](https://github.com/sciencemesh/sciencemesh/issues/196). +Fourth, related to the second one, it removes the partial 'open relay' problem that exists when the Sending Server is allowed to include any Receiving Server FQDN in the Sending Gesture. Without the use of Invites, a Distributed Denial of Service attack could be organised if many internet users collude to flood a given OCM Server with Share Creation Notifications which will be hard to distinguish from legitimate requests without human interaction. An unsolicited (invalid) Invite Acceptance Request is much easier to filter out than an unsolicited (possibly valid, possibly invalid) OCM request, since the Invite Acceptance Request needs to contain an Invite Token that was previously uniquely generated at the Invite Sender OCM server. ### OCM API Discovery After establishing contact as discussed in the previous section, the Sharing User can send the Share Creation Gesture to the Sending Server, providing the Sending Server with the following information: From 564926cafddc49303aea79d8fd3f96447e8ca283 Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Thu, 5 Sep 2024 12:19:46 +0200 Subject: [PATCH 08/15] remove references in Invite flow section --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9be1027..5806878 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ We define the following concepts (with some non-normative references to related * __Sending Gesture__ - a user interface interaction from the Sending Party to the Sending Server, conveying the intention to create a Share * __Share Creation__ - the addition of a Share to the database state of the Sending Server, in response to a successful Sending Gesture or for another reason * __Share Creation Notification__ - a server-to-server request from the sending server to the receiving server, notifying the receiving server that a Share has been created +* __FQDN__ - Fully Qualified Domain Name, such as `"cloud.example.com"` * __OCM Address__ - a string of the form `@` which can be used to uniquely identify a user or group "at" an OCM-capable server. `` is an opaque string, unique at the server. `` is the Fully Qualified Domain Name by which the server is identified. This can, but doesn't need to be, the domain at which the OCM API of that server is hosted. * __OCM Notification__ - a message from the Receiving Server to the Sending Server or vice versa, using the OCM Notifications endpoint. @@ -80,6 +81,8 @@ unique at the server. `` is the Fully Qualified Domain Name by which the s * __Invite Acceptance Gesture__ - gesture from the Invite Receiver to the Invite Receiver OCM Server, supplying the Invite Token as well as the OCM Address of the Invite Sender, effectively allowlisting the Invite Sender OCM Server for sending Share Creation Notifications to the Invite Receiver OCM Server. * __Invite Acceptance Request__ - API call from the Invite Receiver OCM Server to the Invite Sender OCM Server, supplying the Invite Token as well as the OCM Address of the Invite Receiver, effectively allowlisting the Invite Sender OCM Server for sending Share Creation Notifications to the Invite Receiver OCM Server. * __Invite Acceptance Response__ - HTTP response to the Invite Acceptance Request +* __Share Name__ - a human-readable string, provided by the Sending Party or the Sending Server, to help the Receiving Party understand which Resource the Share grants access to +* __Share Permissions__ - protocol-specific restrictions on the modes of accessing the Resource ### General Flow The lifecycle of an Open Cloud Mesh Share starts with prerequisites such as @@ -169,8 +172,8 @@ After establishing contact as discussed in the previous section, the Sharing Use * Sending Party's identifier * Receiving Party's identifier * Receiving Server FQDN -* OPTIONAL: Name -* OPTIONAL: Permissions +* OPTIONAL: Share Name +* OPTIONAL: Share Permissions The next step is for the Sending Server to additionally discover: * if the Receiving Server is trusted @@ -179,16 +182,16 @@ The next step is for the Sending Server to additionally discover: * at which URL * the public key the Receiving Server will use for HTTP Signatures (if any) -The Sending Server MAY first preform denylist and allowlist checks on the FQDN. +The Sending Server MAY first perform denylist and allowlist checks on the FQDN. -If a finite allowlist of receiver servers exists on the sender side, then this list may already contain all necessary information. +If a finite allowlist of Receiving Servers exists on the Sending Server side, then this list may already contain all necessary information. If the FQDN passes the denylist and/or allowlist checks, but no details about its OCM API are known, the Sending Server can use the following process to try to fetch this information from the Receiving Server. This process MAY be influenced by a VPN connection and/or IP allowlisting. -Otherwise, for instance -when a sending server allows sharing to any internet-hosted receiving server, then trust can be established dynamically, and OCM API discovery can happen from the Receiving Server FQDN, using `https:///.well-known/ocm` (or `https:///ocm-provider`, for backwards compatibility) as the URL. The Receiving Server SHOULD provide both of these. See the [API specification](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1.well-known~1ocm/get) for a normative definition of both endpoints. +OCM API discovery can happen from the Receiving Server FQDN, using a HTTP GET request to `https:///.well-known/ocm` (or `https:///ocm-provider`, for backwards compatibility). +The Receiving Server SHOULD provide both of these. See the [API specification](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1.well-known~1ocm/get) for a normative definition of both endpoints. To help with situations where hosting `https:///.well-known/ocm` or `https:///ocm-provider` is impractical, a further discovery mechanism MAY be provided, based on DNS `SRV` Service Records. From 5e1b8e9a22a07e15fcc25db1dcd864486a3079f8 Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Thu, 5 Sep 2024 14:19:35 +0200 Subject: [PATCH 09/15] Refer to I-D spec from API spec --- README.md | 10 ++++++++++ spec.yaml | 8 +++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5806878..015c7e5 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,10 @@ Whereas the precise syntax of the Invite Message and the Invite Acceptance Gestu The Invite Receiver OCM Server SHOULD apply its own policies for trusting the Invite Sender OCM Server before making the Invite Acceptance Request. +Since the Invite Flow does not require either Party to type or remember the `userId`, this string does not need to be human-memorable. Even if the Invite Receiver has a memorable username at the Invite Receiver OCM Server, this `userId` that forms part of their OCM Address does not need to match it. + +Also, a different `userId` could be given out to each contact, to avoid correlation of identities. + ##### Invite Acceptance Response Details The Invite Acceptance Response SHOULD be a HTTP response: * in response to the Invite Acceptance Request @@ -151,11 +155,17 @@ The Invite Acceptance Response SHOULD be a HTTP response: The Invite Sender OCM Server SHOULD verify the HTTP Signature on the Invite Acceptance Request and apply its own policies for trusting the Invite Receiver OCM Server before processing the Invite Acceptance Request and sending the Invite Acceptance Response. +As with the `userId` in the Invite Acceptance Request, the one in the Response also doesn't need to be human-memorable, doesn't need to match the Invite Sender's username at their OCM Server + ##### Addition into address books Following these step, both servers MAY display the `name` of the other party as a trusted or allowlisted contact, and enable selecting them as a Receiving Party. OCM Servers MAY enforce a policy to only accept Share Creation Notifications from such trusted contacts, or MAY display a warning to users when a Share Creation Notification from an unknown party is received. Both servers MAY also allowlist each other as a server with which at least one of their users wishes to interact. +Note that Invites act symmetrically, so once contact has been established, both the Invite Sender and the Invite Receiver may take on either the Sending Party or the Receiving Party role in subsequent Share Creation events. + +Both parties may delete the other party from their address book at any time without notifying them. + ##### Security Advantages It is important to underscore the value of the Invite in this scenario, as it provides four important security advantages. First of all, if the Receiving Server blocks Share Creation Notifications from Sending Parties who are not in the addressbook of the Receiving Party, then this protects the Receiving Party from receiving unsolicited Shares. An attacker could still send the Receiving Party an unsolicited Share, but they would first need to convince the Receiving Party through an out-of-band communication channel to accept their invite. In many use cases, the Receiving Party has had other forms of contact with the Sending Party (e.g. in-person or email back-and-forth). The out-of-band Invite Message thus leverages the filters and context which the Receiving Party may already benefit from in that out-of-band communication. For instance, a careful Receiving Party may choose to only accept Invites that reach them via a private or moderated messaging platform. diff --git a/spec.yaml b/spec.yaml index f1f9a53..3d1504b 100644 --- a/spec.yaml +++ b/spec.yaml @@ -206,12 +206,10 @@ paths: $ref: "#/definitions/Error" /invite-accepted: post: - summary: Inform the sender that an invitation was accepted to start sharing + summary: [Invite Acceptance Request](https://github.com/cs3org/OCM-API/tree/internet-draft-format?tab=readme-ov-file#invite-acceptance-request-details) and [Response](https://github.com/cs3org/OCM-API/tree/internet-draft-format?tab=readme-ov-file#invite-acceptance-response-details) description: > - Inform about an accepted invitation so the user on the sender provider's side can initiate the OCM share creation. - To protect the identity of the parties, for shares created following an OCM invitation, - the user id MAY be hashed, and recipients implementing the OCM invitation workflow - MAY refuse to process shares coming from unknown parties. + See the Open Cloud Mesh [Invite flow spec](https://github.com/cs3org/OCM-API/tree/internet-draft-format?tab=readme-ov-file#invite-flow) for more details. + parameters: - name: invite in: body From 2116d37dbfd619a56e6b2cea596036781368803e Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Thu, 5 Sep 2024 14:21:39 +0200 Subject: [PATCH 10/15] userId -> userID --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 015c7e5..0892155 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ Whereas the precise syntax of the Invite Message and the Invite Acceptance Gestu * its request body containing a JSON document representing an object with the following string fields: * `recipientProvider` - FQDN of the Invite Receiver OCM Server * `token` - the Invite Token. The Invite Sender OCM Server SHOULD recall which Invite Sender OCM Address this token was linked to - * `userId` - the Invite Receiver's identifier at their OCM Server + * `userID` - the Invite Receiver's identifier at their OCM Server * `email` - non-normative / informational; an email address for the Invite Receiver. Not necessarily at the same FQDN as their OCM Server * `name` - human-readable name of the Invite Receiver, as a suggestion for display in the Invite Sender's address book * using TLS @@ -140,22 +140,22 @@ Whereas the precise syntax of the Invite Message and the Invite Acceptance Gestu The Invite Receiver OCM Server SHOULD apply its own policies for trusting the Invite Sender OCM Server before making the Invite Acceptance Request. -Since the Invite Flow does not require either Party to type or remember the `userId`, this string does not need to be human-memorable. Even if the Invite Receiver has a memorable username at the Invite Receiver OCM Server, this `userId` that forms part of their OCM Address does not need to match it. +Since the Invite Flow does not require either Party to type or remember the `userID`, this string does not need to be human-memorable. Even if the Invite Receiver has a memorable username at the Invite Receiver OCM Server, this `userID` that forms part of their OCM Address does not need to match it. -Also, a different `userId` could be given out to each contact, to avoid correlation of identities. +Also, a different `userID` could be given out to each contact, to avoid correlation of identities. ##### Invite Acceptance Response Details The Invite Acceptance Response SHOULD be a HTTP response: * in response to the Invite Acceptance Request * using `application/json` as the `Content-Type` HTTP response header * its response body containing a JSON document representing an object with the following string fields: - * `userId` - the Invite Sender's identifier at their OCM Server + * `userID` - the Invite Sender's identifier at their OCM Server * `email` - non-normative / informational; an email address for the Invite Sender. Not necessarily at the same FQDN as their OCM Server * `name` - human-readable name of the Invite Sender, as a suggestion for display in the Invite Receiver's address book The Invite Sender OCM Server SHOULD verify the HTTP Signature on the Invite Acceptance Request and apply its own policies for trusting the Invite Receiver OCM Server before processing the Invite Acceptance Request and sending the Invite Acceptance Response. -As with the `userId` in the Invite Acceptance Request, the one in the Response also doesn't need to be human-memorable, doesn't need to match the Invite Sender's username at their OCM Server +As with the `userID` in the Invite Acceptance Request, the one in the Response also doesn't need to be human-memorable, doesn't need to match the Invite Sender's username at their OCM Server ##### Addition into address books Following these step, both servers MAY display the `name` of the other party as a trusted or allowlisted contact, and enable selecting them as a Receiving Party. OCM Servers MAY enforce a policy to only accept Share Creation Notifications from such trusted contacts, or MAY display a warning to users when a Share Creation Notification from an unknown party is received. From c6f14ce6c2ae32689aeb830137dd0e524bab7e33 Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Thu, 5 Sep 2024 14:27:10 +0200 Subject: [PATCH 11/15] Bring field descriptions in line with I-D. Ref #117 --- spec.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec.yaml b/spec.yaml index 3d1504b..916f17b 100644 --- a/spec.yaml +++ b/spec.yaml @@ -681,15 +681,15 @@ definitions: example: xyz userID: type: string - description: Unique ID to identify the user at the remote provider accepting the invite. + description: Unique ID to identify the Invite Receiver at their OCM Server. example: 51dc30ddc473d43a6011e9ebba6ca770 email: type: string - description: Email ID of the user accepting the invite. + description: Email address of the Invite Receiver. example: richard@gmail.com name: type: string - description: Name of the user accepting the invite. + description: Name of the Invite Receiver. example: Richard Feynman AcceptedInviteResponse: type: object @@ -697,15 +697,15 @@ definitions: - properties: userID: type: string - description: Unique ID to identify the sender at the local provider. + description: Unique ID to identify the Invite Sender at their OCM Server. example: 9302 email: type: string - description: Email ID of the user that sent the invite. + description: Email ID of the Invite Sender. example: john@sender.org name: type: string - description: Name of the user that sent the invite. + description: Name of the Invite Sender. example: John Doe TokenRequest: type: object From dfc3984a30787651dfd516f17efc42da85cd9769 Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Thu, 5 Sep 2024 14:32:32 +0200 Subject: [PATCH 12/15] response codes --- README.md | 7 ++++++- spec.yaml | 8 ++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0892155..191998a 100644 --- a/README.md +++ b/README.md @@ -153,9 +153,14 @@ The Invite Acceptance Response SHOULD be a HTTP response: * `email` - non-normative / informational; an email address for the Invite Sender. Not necessarily at the same FQDN as their OCM Server * `name` - human-readable name of the Invite Sender, as a suggestion for display in the Invite Receiver's address book +A 200 response status means the Invitation Acceptance Request was successful. +A 400 response status means the Invitation Token is invalid or does not exist. +A 403 response status means the Invite Receiver OCM Server is not trusted to accept this Invite. +A 409 response status means the Invite was already accepted. + The Invite Sender OCM Server SHOULD verify the HTTP Signature on the Invite Acceptance Request and apply its own policies for trusting the Invite Receiver OCM Server before processing the Invite Acceptance Request and sending the Invite Acceptance Response. -As with the `userID` in the Invite Acceptance Request, the one in the Response also doesn't need to be human-memorable, doesn't need to match the Invite Sender's username at their OCM Server +As with the `userID` in the Invite Acceptance Request, the one in the Response also doesn't need to be human-memorable, doesn't need to match the Invite Sender's username at their OCM Server. ##### Addition into address books Following these step, both servers MAY display the `name` of the other party as a trusted or allowlisted contact, and enable selecting them as a Receiving Party. OCM Servers MAY enforce a policy to only accept Share Creation Notifications from such trusted contacts, or MAY display a warning to users when a Share Creation Notification from an unknown party is received. diff --git a/spec.yaml b/spec.yaml index 916f17b..636a88b 100644 --- a/spec.yaml +++ b/spec.yaml @@ -219,19 +219,19 @@ paths: $ref: "#/definitions/AcceptedInvite" responses: 200: - description: Invitation accepted. + description: Invitation Acceptance Request successful (see [Invite Acceptance Response](https://github.com/cs3org/OCM-API/tree/internet-draft-format?tab=readme-ov-file#invite-acceptance-response-details)) schema: $ref: "#/definitions/AcceptedInviteResponse" 400: - description: The invitation token is invalid or does not exist. + description: The Invitation Token is invalid or does not exist (see [Invite Acceptance Response](https://github.com/cs3org/OCM-API/tree/internet-draft-format?tab=readme-ov-file#invite-acceptance-response-details)) schema: $ref: "#/definitions/Error" 403: - description: Remote service is not trusted to accept invitations. + description: Invite Receiver OCM Server is not trusted to accept this Invite (see [Invite Acceptance Response](https://github.com/cs3org/OCM-API/tree/internet-draft-format?tab=readme-ov-file#invite-acceptance-response-details)) schema: $ref: "#/definitions/Error" 409: - description: Invitation already accepted. + description: Invitation already accepted (see [Invite Acceptance Response](https://github.com/cs3org/OCM-API/tree/internet-draft-format?tab=readme-ov-file#invite-acceptance-response-details)) schema: $ref: "#/definitions/Error" /token: From f6e363451bcf01afea842cf3604609847be7b2b3 Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Thu, 5 Sep 2024 16:36:48 +0200 Subject: [PATCH 13/15] Discovery document fields from API spec to I-D spec --- README.md | 92 +++++++++++++++++++++++++++++++++++++++++++++++++------ spec.yaml | 21 ++++++------- 2 files changed, 92 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 191998a..18d3eeb 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,9 @@ the equivalent [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (fka Swag * [Invite Acceptance Response Details](#invite-acceptance-response-details) * [Further Reading](#further-reading) * [OCM API Discovery](#ocm-api-discovery) + * [Introduction](#introduction-1) + * [Process](#process) + * [Fields](#fields) * [Share Creation Notification](#share-creation-notification) * [Receiving Party Notification](#receiving-party-notification) * [Share Acceptance Notification](#share-acceptance-notification) @@ -68,8 +71,13 @@ We define the following concepts (with some non-normative references to related * __Share Creation__ - the addition of a Share to the database state of the Sending Server, in response to a successful Sending Gesture or for another reason * __Share Creation Notification__ - a server-to-server request from the sending server to the receiving server, notifying the receiving server that a Share has been created * __FQDN__ - Fully Qualified Domain Name, such as `"cloud.example.com"` -* __OCM Address__ - a string of the form `@` which can be used to uniquely identify a user or group "at" an OCM-capable server. `` is an opaque string, +* __OCM Server__ - a server that supports OCM. +* __Discovering Server__ - a server that tries to obtain information in OCM API discovery +* __Discoverable Server__ - a server that tries to supply information in OCM API discovery +* __OCM Address__ - a string of the form `@` which can be used to uniquely identify a user or group "at" an OCM Server. `` is an opaque string, unique at the server. `` is the Fully Qualified Domain Name by which the server is identified. This can, but doesn't need to be, the domain at which the OCM API of that server is hosted. +* __Vanity OCM Address__ - a string that looks like an OCM Address but with an alternative (generally shorter or nicer) FQDN. This FQDN does not support HTTP-based discovery, but it does provide an SRV record in its DNS zone, pointing to the (generally longer or uglier) FQDN of the OCM Server. +* __Regular OCM Address__ - an OCM Address that is not a Vanity OCM Address * __OCM Notification__ - a message from the Receiving Server to the Sending Server or vice versa, using the OCM Notifications endpoint. * __Invite Message__ - out-of-band message used to establish contact between parties and servers in the Invite Flow, containing an Invite Token (see below) and the Invite Sender's OCM Address * __Invite Sender__ - the party sending an Invite @@ -181,6 +189,7 @@ Third, equivalently, the Sending Server knows it is essentially registering the Fourth, related to the second one, it removes the partial 'open relay' problem that exists when the Sending Server is allowed to include any Receiving Server FQDN in the Sending Gesture. Without the use of Invites, a Distributed Denial of Service attack could be organised if many internet users collude to flood a given OCM Server with Share Creation Notifications which will be hard to distinguish from legitimate requests without human interaction. An unsolicited (invalid) Invite Acceptance Request is much easier to filter out than an unsolicited (possibly valid, possibly invalid) OCM request, since the Invite Acceptance Request needs to contain an Invite Token that was previously uniquely generated at the Invite Sender OCM server. ### OCM API Discovery +#### Introduction After establishing contact as discussed in the previous section, the Sharing User can send the Share Creation Gesture to the Sending Server, providing the Sending Server with the following information: * Resource to be shared * Protocol to be offered for access @@ -205,14 +214,79 @@ If the FQDN passes the denylist and/or allowlist checks, but no details about it This process MAY be influenced by a VPN connection and/or IP allowlisting. -OCM API discovery can happen from the Receiving Server FQDN, using a HTTP GET request to `https:///.well-known/ocm` (or `https:///ocm-provider`, for backwards compatibility). -The Receiving Server SHOULD provide both of these. See the [API specification](https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1.well-known~1ocm/get) for a normative definition of both endpoints. - -To help with situations where hosting `https:///.well-known/ocm` or `https:///ocm-provider` is impractical, a further discovery mechanism MAY be provided, based on DNS `SRV` Service Records. - -If `https:///.well-known/ocm` does not exist, the Receiving Server MAY instead point to `https:///.well-known/ocm` by ensuring that a `type=SRV` DNS query to `_ocm._tcp.` resolves to e.g. `service = 10 10 443 ` - -When attempting to discover the OCM API details for ``, if https:///.well-known/ocm can not be fetched, implementations SHOULD fall back to querying the corresponding `_ocm._tcp.` DNS record, e.g. `_ocm._tcp.provider.org`, and subsequently make a HTTP GET request to the host returned by that DNS query, followed by the `/.well-known/ocm` URL path, using TLS. +When OCM API discovery can occur in preparation of a Share Creation Notification, the Sending Server takes on the 'Discovering Server' role and the Receiving Server plays the role of 'Discoverable Server'. + +#### Process +At the start of the process, the Discovering Server has either an OCM Address, or just an FQDN from for instance the `recipientProvider` field of an Invite Acceptance Request. + +Step 1: In case it has an OCM Address, it should first extract `` from it (the part after the `@` sign). +Step 2: The Discovering Server SHOULD attempt OCM API discovery a HTTP GET request to `https:///.well-known/ocm`. +Step 3: If that results in a valid HTTP response with a valid JSON response body within reasonable time, go to step 8. +Step 4: If not, try a HTTP GET with `https:///ocm-provider` as the URL instead. +Step 5: If that results in a valid HTTP response with a valid JSON response body within reasonable time, go to step 8. +Step 6: If not, and the `` came from an OCM Address, then it SHOULD check if the OCM Address in question was a Vanity OCM Address. +This can be checked with a `type=SRV` DNS query to `_ocm._tcp.`. If that returns `service = 10 10 443 ` then repeat from step 2, using `` instead of the `` that appeared in the Vanity OCM Address. +Step 7: If not, fail. +Step 8: The JSON response body is the data that was discovered. + +#### Fields +The JSON response body offered by the Discoverable Server SHOULD contain the following information about its OCM API: + +* REQUIRED: enabled (boolean) - Whether the OCM service is enabled at this endpoint +* REQUIRED: apiVersion (string) - The OCM API version this endpoint supports. Example: `"1.1.0"` +* REQUIRED: endPoint (string) - The URI of the OCM API available at this endpoint. Example: `"https://my-cloud-storage.org/ocm"` +* OPTIONAL: provider (string) - A friendly branding name of this endpoint. Example: `"MyCloudStorage"` +* REQUIRED: resourceTypes (array) - A list of all supported resource types with their access protocols. Each item in this list should +itself be an object containing the following fields: + * name (string) - A supported resource type (file, folder, calendar, contact, ...). + Implementations MUST support `file` at a minimum. Each resource type is identified by its `name`: the list MUST NOT + contain more than one resource type object per given `name`. + * shareTypes (array of string) - + The supported recipient share types. + MUST contain `"user"` at a minimum, plus optionally `"group"` and `"federation"`. + Example: `["user"]` + * protocols (object) - The supported protocols for accessing shared resources. + Implementations MUST support at least `webdav` for `file` resources, + any other combination of resources and protocols is optional. Example: + ```json + { + "webdav": "/remote/dav/ocm/", + "webapp": "/app/ocm/", + "talk": "/apps/spreed/api/" + } + ``` + Fields: + * webdav (string) - The top-level WebDAV path at this endpoint. In order to access + a remote shared resource, implementations MAY use this path + as a prefix, or as the full path (see sharing examples). + * webapp (string) - The top-level path for web apps at this endpoint. This value + is provided for documentation purposes, and it SHALL NOT + be intended as a prefix for share requests. + * datatx (string) - The top-level path to be used for data transfers. This + value is provided for documentation purposes, and it SHALL + NOT be intended as a prefix. In addition, implementations + are expected to execute the transfer using WebDAV as + the wire protocol. + * 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. + +* OPTIONAL: capabilities (array of string) - The optional capabilities supported by this OCM Server. + As implementations MUST accept Share Creation Notifications to be compliant, + it is not necessary to expose that as a capability. + Example: `["/notifications"]`. The array MAY include for instance: + * `"/notifications"` - to indicate this OCM server is capable of processing OCM Notifications + * `"/invite-accepted"` - to indicate that this OCM server is capable of processing Invite Acceptance Requests. + * `"/mfa-capable"` - to indicate that this OCM server can apply a Sending Server's MFA requirements for a Share on their behalf. + +* OPTIONAL: publicKey (object) - The signatory used to sign outgoing request to confirm its origin. The + signatory is optional, but if present, it MUST contain two string fields, `id` and `publicKeyPem`. + properties: + * REQUIRED id (string) 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 + * REQUIRED publicKeyPem (string) - PEM-encoded version of the public key. + Example: "-----BEGIN PUBLIC KEY-----\nMII...QDD\n-----END PUBLIC KEY-----\n" ### Share Creation Notification To create a share, the sending server SHOULD make a HTTP POST request diff --git a/spec.yaml b/spec.yaml index 636a88b..ec158a4 100644 --- a/spec.yaml +++ b/spec.yaml @@ -30,31 +30,28 @@ parameters: the HAL navigation links (e.g. `_links.next.href`) to paginate. These links enable the possibility to use vendor specific pagination. paths: - /.well-known/ocm: + https:///.well-known/ocm: get: summary: Discovery endpoint description: > Following RFC 8615, this endpoint returns the properties and capabilities - offered by a remote cloud storage. This endpoint is to be served as a - top-level URL, e.g. as in `https://my-cloud-storage.org/.well-known/ocm`. + offered by an OCM Server. This endpoint is to be served at the OCM server's FQDN, + or at the discovery FQDN that its SRV DNS record redirects to, + e.g. as in `https://my-cloud-storage.org/.well-known/ocm`. + See [OCM API Discovery](https://github.com/cs3org/OCM-API/tree/internet-draft-format?tab=readme-ov-file#ocm-api-discovery) for more details. responses: "200": - description: The capabilities of this OCM service + description: The API endpoint, version, public key, and capabilities of this OCM Server schema: $ref: "#/definitions/Discovery" - /ocm-provider: + https:///ocm-provider: get: summary: Legacy discovery endpoint description: > - This endpoint returns the properties and capabilities offered by a remote - cloud storage. It is a replica of `/.well-known/ocm` and it is required - to be supported, owing to previously established practices by the main - cloud storages that have implemented OCM (for more details, see - https://github.com/cs3org/OCM-API/pull/37#issuecomment-435875108). - This endpoint is to be served as a top-level URL. + This endpoint is a replica of `/.well-known/ocm`. OCM Servers MUST support both. responses: "200": - description: The capabilities of this OCM service + description: See `/.well-known/ocm`. schema: $ref: "#/definitions/Discovery" /shares: From 31debecd79932689b882af0c78a1ee0c3589ff22 Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Thu, 5 Sep 2024 16:38:57 +0200 Subject: [PATCH 14/15] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 18d3eeb..dcaa0f3 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ We define the following concepts (with some non-normative references to related * provides access to it (by exposing at least one "API"), * takes the decision to create the Share based on user interface gestures from the Sending Party (the "Authorization Server" role in OAuth) * takes the decision about authorizing attempts to access the Resource (the "Resource Server" role in OAuth) - * send out Share Creation Notifications when appropriate (see below) + * sends out Share Creation Notifications when appropriate (see below) * __Receiving Server__ - the server that: * receives Share Creation Notifications (see below) * actively or passively notifies the receiving user or group of any incoming Share Creation Notification From 0698620d169f9f715999201f898d80bd983a770e Mon Sep 17 00:00:00 2001 From: Mahdi Baghbani Date: Thu, 5 Sep 2024 18:51:23 +0330 Subject: [PATCH 15/15] refactor: hal+json -> json as response type --- spec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec.yaml b/spec.yaml index ec158a4..8cd883b 100644 --- a/spec.yaml +++ b/spec.yaml @@ -10,7 +10,7 @@ schemes: consumes: - application/json produces: - - application/hal+json + - application/json parameters: id: name: id