cs3org / reva

WebDAV/gRPC/HTTP high performance server to link high level clients to storage backends
https://reva.link
Apache License 2.0
167 stars 113 forks source link

Changes to appregistry and the HTTP appprovider service #1923

Open ishank011 opened 3 years ago

ishank011 commented 3 years ago

To keep track of the changes left from https://github.com/cs3org/reva/pull/1785:

ishank011 commented 3 years ago

The changes to be made after the discussion today:

  1. Need to add a new endpoint to the HTTP service which would return all the supported mime types along with the apps that they can be opened with. This would utilize the newly added ListSupportedMimeTypes CS3APIs call. The response would look like:
{
    "mime-types": [
        "text/markdown": [{app_name: "Codimd"}],
        "application/vnd.oasis.opendocument.text": [{app_name: "Collabora"}, {app_name: "OnlyOffice"}, {app_name: "MS Office"}],
        "application/vnd.oasis.opendocument.spreadsheet": [{app_name: "Collabora"}, {app_name: "OnlyOffice"}, {app_name: "MS Office"}],
        ...
    ]
}

Note that we're returning the mime types here instead of the extension because we can add custom types in reva itself and the clients don't need to know about those. Related https://github.com/owncloud/web/issues/5135

We can also return other stuff such as the link to a static asset representing the icon corresponding to the app. Some apps might want to execute some action (eg, via a JS script) so we need to see how those can be passed, but that can be decided later.

  1. The open HTTP endpoint in reva needs to return app-specific (but agnostic to the app provider service) headers and form parameters which would be utilized by web to call the app URL. The main motive is to avoid passing these in the URL which can otherwise expose access tokens following the WOPI specs. After doing a requirement analysis of which apps we're planning on supporting, this payload seems to handle almost all such cases. @wkloucek can you add here your notes about what parameters each app expects? The open request would have the following parameters
    • file_id
    • app_name (optional; if missing, the default app is used; if default is not set, the first app in the list is used)
    • view_mode (optional; if missing, we'll use the max permissions the user has on the resource)

To help web identify how the call has to be made to the app, an extra parameter method will be added. For example, two responses can look like:

{
    "app-url": "collabora.owncloud.com/loleaflet/u-u-i-d/loleaflet.html?src=...",
    "method": "POST",
    "form-parameters": {
        "access-token": "reva-token",
        "access-token-ttl": "when-reva-token-expires"
    }
}

{
    "app-url": "jupyterlabs.owncloud.com/src=...",
    "method": "GET",
    "headers": {
        "user-language": "en",
        "user-session-id": 1234
    }
}
  1. The response from WOPI would also be in the form of a JSON object instead of the current way of encoding all parameters in the URI. cc @glpatcern

  2. Based on the permissions and mime-type obtained from the PROPFIND response, web will display the possible actions for each file. Currently, we don't have user-specific or group-specific information or user-defaults so all users and all storages have access to all allowed apps, but this will be the next step to be added. cc @wkloucek

  3. We need to come up with a design to display all the specific actions in web. For example, for a word file, we have 3 apps x 3 view modes. Displaying 9 actions in the drop-down menu doesn't really make up a good UX. @kulmann @pascalwengerter @fschade

Please add any other points that I might have missed @labkode @wkloucek @glpatcern

glpatcern commented 3 years ago

Thanks Ishank for the nice summary. Following your list:

  1. I'll create a PR in the wopiserver, I expect we could provide a response to /wopi/iop/openinapp that fully matches the GRPC or HTTP AppProviderResponse, such as:

    {
    "app-url" : "collabora.owncloud.com/...?WOPISrc=...",
    "form-parameters" : { "access_token" : "xyz", "access_token_ttl": 123 }
    }

    (Side-note: who decides the method? shall we rather assume that if form-parameters are empty then method = GET, else POST?)

  2. Just to report my comment: to simplify the UI, in first approximation for the Word case (3x3 actions) one could show a single View action (opens in the default app with READ_ONLY as viewMode) plus the 3 Open with <appname> actions (all in edit mode).

ishank011 commented 3 years ago

@glpatcern I think the token dismantling should happen in the driver, since the TTL would be decided by the reva token. Maybe wopiserver can just return the token itself.

(Side-note: who decides the method? shall we rather assume that if form-parameters are empty then method = GET, else POST?)

I think this should also be passed from the driver. That way we have a complete URL object being returned as part of the CS3APIs call (URL, form, headers, method). What do you think?

Just to report my comment: to simplify the UI, in first approximation for the Word case (3x3 actions) one could show a single View action (opens in the default app with READ_ONLY as viewMode) plus the 3 Open with actions (all in edit mode).

+1

ishank011 commented 3 years ago

For the listing endpoint at /app/list/, we have

$ curl -v -k -X GET 'http://localhost:19001/app/list' | jq
{
  "mime_types": {
    "application/msword": {
      "app_provider_names": [
        "Collabora",
        "Microsoft Office 365"
      ]
    },
    "application/octet-stream": {
      "app_provider_names": [
        "Collabora",
        "Microsoft Office 365"
      ]
    },
    "application/pdf": {
      "app_provider_names": [
        "Microsoft Office 365"
      ]
    },
    "application/rtf": {
      "app_provider_names": [
        "Collabora",
        "Microsoft Office 365"
      ]
    },
    ...
  }
}

We can expand this so instead of just the names, we return the whole provider struct which could have the icon URI, description and so on.

glpatcern commented 3 years ago

@glpatcern I think the token dismantling should happen in the driver, since the TTL would be decided by the reva token. Maybe wopiserver can just return the token itself.

You're absolutely right, my mistake. The wopiserver will definitely not dismantle the Reva token and will only return { "access_token" : "xyz"} - which is on its own not to be dismantled by Reva, so responsibilities stay separated.

I think this should also be passed from the driver. That way we have a complete URL object being returned as part of the CS3APIs call (URL, form, headers, method). What do you think?

Why not, but then if we want the driver to decide the method it could do so based on a config parameter, or on some kind of defaults. E.g. the wopi driver could default to POST (assuming all WOPI apps support it), the swan driver could default to GET, etc.

wkloucek commented 3 years ago

After doing a requirement analysis of which apps we're planning on supporting, this payload seems to handle almost all such cases. @wkloucek can you add here your notes about what parameters each app expects?

wkloucek commented 3 years ago

I also had a discussion with my colleagues and they added following feedback:

labkode commented 3 years ago

@wkloucek I'll be discussing with @butonic today about a way to have finer controls for sharing permissions and I can fold in this requirement for view-only mode.

glpatcern commented 3 years ago

I concur we need a view-only "value" in the set of permissions, and I started to do some some work in this direction in the context of the deny permission implementation. Let's review what we have.

ishank011 commented 3 years ago

we could introduce some scope for the ListSupportedMimeTypes dependent on which client is requesting the list.

I think we can add this filtering to the HTTP service instead of CS3APIs as it doesn't really fit there.

ishank011 commented 3 years ago

HTTP response for Collabora, which supports POST

» curl -v -k -X POST 'http://localhost:19001/app/open?file_id=MTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjU1NDQwMDAwOmZpbGVpZC1laW5zdGVpbiUyRnNhbXBsZS5kb2M=&view_mode=write&app_name=Collabora' -u einstein:relativity | jq

* Server auth using Basic with user 'einstein'
> POST /app/open?file_id=MTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjU1NDQwMDAwOmZpbGVpZC1laW5zdGVpbiUyRnNhbXBsZS5kb2M=&view_mode=write&app_name=Collabora HTTP/1.1
> Authorization: Basic ZWluc3RlaW46cmVsYXRpdml0eQ==
> User-Agent: curl/7.29.0
> Host: localhost:19001
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Vary: Origin
< Date: Tue, 10 Aug 2021 14:43:19 GMT
< Content-Length: 1842
<
{
  "app_url": "https://collabora-online.cern.ch/byoa/collabora/loleaflet/78239aa/loleaflet.html?&WOPISrc=https%3A%2F%2Fcbox-wopiocis-01.cern.ch%3A8443%2Fwopi%2Ffiles%2F123e4567-e89b-12d3-a456-426655440000-ZmlsZWlkLWVpbnN0ZWluJTJGc2FtcGxlLmRvYw%3D%3D",
  "method": "POST",
  "form_parameters": {
    "access_token": "eyJ0eXAiOiJKV1QiLC...",
    "access_token_ttl": "1628693306"
  }
}

Response for CodiMD, which doesn't

» curl -v -k -X POST 'http://localhost:19001/app/open?file_id=MTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjU1NDQwMDAwOmZpbGVpZC1laW5zdGVpbiUyRmEubWQ=&view_mode=write&app_name=CodiMD' -u einstein:relativity | jq

* Server auth using Basic with user 'einstein'
> POST /app/open?file_id=MTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjU1NDQwMDAwOmZpbGVpZC1laW5zdGVpbiUyRmEubWQ=&view_mode=write&app_name=CodiMD HTTP/1.1
> Authorization: Basic ZWluc3RlaW46cmVsYXRpdml0eQ==
> User-Agent: curl/7.29.0
> Host: localhost:19001
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Vary: Origin
< Date: Tue, 10 Aug 2021 14:41:28 GMT
< Content-Length: 1824
<
{
  "app_url": "https://qa.cernbox.cern.ch/byoa/codimd/GkG6Q6WLEd4GeGc7fm7EQlQ31H8?metadata=https%3A%2F%2Fcbox-wopiocis-01.cern.ch%3A8443%2Fwopi%2Ffiles%2F123e4567-e89b-12d3-a456-426655440000-ZmlsZWlkLWVpbnN0ZWluJTJGYS5tZA%3D%3D%3Ft%3DeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2Vya...&apiKey=testsecret&displayName=einstein",
  "method": "GET"
}
ishank011 commented 3 years ago

Wopiserver changes by @glpatcern: https://github.com/cs3org/wopiserver/pull/43

wkloucek commented 3 years ago

One thing we didn't talk about is what we should display in the New file dialog: image

Technically we just have the mimetypes (see https://github.com/cs3org/reva/issues/1923#issuecomment-891908827) available, but these are not really user friendly (eg. create new application/vnd.oasis.opendocument.text doesn't come in handy for non technical people).

Do you have any ideas? Maybe add a human friendly file name to the mimetype? (I see L10N challenges incoming....)

cc @kulmann @fschade

labkode commented 3 years ago

@wkloucek maybe we can expand the mapping of mimetypes with a new field "display name" that is more user friendly?

wkloucek commented 3 years ago

@wkloucek maybe we can expand the mapping of mimetypes with a new field "display name" that is more user friendly?

there is PR on that in the CSapis: https://github.com/cs3org/cs3apis/pull/145/files. Will have a look next week

glpatcern commented 3 years ago

Yep, I just created that PR for this use case indeed