Roundup Tracker - Issues

Issue 2551068

classification
Provide way to retrieve file/msg data via rest endpoint.
Type: rfe Severity: normal
Components: API Versions:
process
Status: fixed fixed
:
: rouilj : rouilj, schlatterbeck
Priority: : rest

Created on 2019-10-28 00:29 by rouilj, last changed 2024-12-08 22:46 by rouilj.

Messages
msg6780 Author: [hidden] (rouilj) Date: 2019-10-28 00:29
From msg6749:
====
... I wonder if we should provide a
way via the rest interface to download just the content data in raw
form.

I think the right way to do this is to make the request to
demo/data/file/11/content but set the header:

 Accept:  image/vnd.microsoft.icon

If the content type matches the file type, respond with a binary data
stream with appropriate Content-Type (either the same as the Accept
type or application/octet-stream) and Content-Length. If it doesn't
match we return 406 - not acceptable.
===

If we do not set an acept header, or set accept to application/json
or application/xml (if enabled) we get back the standard data wrapped
output like (json format):

  {
    "data": {
        "id": "1",
        "type": "file",
        "link": "https://.../demo/rest/data/file/1",
        "attributes": {
            "content": {
                "link": "https://.../demo/file1/"
            },
            "name": "android-chrome-192x192.png",
            },
            "type": "image/png"
        },
        "@etag": "\"94c0e14629da6944d9ab2a83546d0737\""
    }
  }


Details and discussion are on: issue2551067 specifically at msg6749
msg7823 Author: [hidden] (rouilj) Date: 2023-08-02 21:59
Also see issue 2551289 for changes to handling Accept headers that are not
json or xml. This should allow downloading the binary_content in raw format
without encoding.
msg8212 Author: [hidden] (rouilj) Date: 2024-12-08 07:18
A new method determine_output_format(self, uri) was added in issue 2551289 to refactor
the dispatch method.

One thought could be to modify determine_output_format() to check for a trailing
'binary_content' element in the path. For example:

   curl -X GET -u admin -H "Accept: image/png" -H "X-Requested-With: rest" \
       https://example.com/tracker/rest/data/file/54/binary_content

results in:

  self.client.request.path '/tracker/rest/data/file/54/binary_content'

Parsing the path and splitting the designator allows getting the mime type of the file
via self.client.db.file.get('54', 'type') -> 'image/png'

This can be compared to the Accept header in the `for part in accept_header:`
loop by adding:

  if part[0] == file_type:
     accept_type = file_type

this should result in a call to get_attribute(self, class_name, item_id, attr_name, input)
which returns the binary data in a dict. Which then calls format_dispatch_output().

Then changing format_dispatch_output(self, accept_mime_type, output, pretty_print)
to add the right content type header and return the output['data']['data'] element
if the accept_mime_type is defined. (Note: accept_mime_type should never be
undefined/None with the change to fix issue 2551289.)

Also in the debugger using open('file', 'wb').write(output['data']['data'] wrote
my test png file just fine. Just need to make sure I can write raw binary data to
the http socket.
msg8213 Author: [hidden] (rouilj) Date: 2024-12-08 07:27
For msg type, I need to handle the case where type is empty. Seems that msg doesn't
always set that type. I assume text/plain (or text/x-rst or text/markdown) would
be suitable depending on what the frontend support for markup.

Maybe support a mime type of text/* in Accept??

For text output, that seems reasonable while binary formats should not
allow a wildcard subtype.
msg8214 Author: [hidden] (rouilj) Date: 2024-12-08 07:36
Other tricks:


So this is an easy way of determining if my db class is a Fileclass.

  (Pdb) from roundup.hyperdb import FileClass
  (Pdb) isinstance( self.db.msg, FileClass)
  True
  (Pdb) isinstance( self.db.status, FileClass)
  False
  (Pdb) isinstance( self.db.file, FileClass)
  True

I think this works regardless of the back end as:

   (Pdb) p self.db.msg.__class__
   <class 'roundup.backends.back_sqlite.FileClass'>

could work using string comparison of the right type (ugh).

This should work if I need to access the data by parsing the path/designator.
 
 self.db.getclass('msg').get("2", "binary_content")

but I think just passing it through dispatch will do the trick.
msg8216 Author: [hidden] (rouilj) Date: 2024-12-08 16:06
Handle application/octet-stream as a universal download mime type.

However without the 'X-Content-Type-Options: nosniff' security header, an html
file with application/octet-stream could be parsed by the browser and
displayed/executed as html.

So set the X-Content-Type-Options header when using this codepath.
client.py sets this header when the SendFile exception is raised for the same reason.
msg8217 Author: [hidden] (rouilj) Date: 2024-12-08 21:54
The existing example in rest.txt shows the content property using the html interface.

            "content": {
                "link": "https://.../demo/msg11/"
            },

I think this is ok since we are using the binary_content property for retrieval.

Also text/* is supported for file/msg without a mime type. I'm not sure if there are
security implications for this. Could somebody craft a binary file and set it without
a mime type and do something strange? We set header to block browser sniffing,
so I think it's ok.
msg8218 Author: [hidden] (rouilj) Date: 2024-12-08 22:46
Committed first attempt in changeset:   8180:d02ce1d14acd.

CI passed. Closing.
History
Date User Action Args
2024-12-08 22:46:16rouiljsetstatus: open -> fixed
resolution: fixed
messages: + msg8218
2024-12-08 21:55:00rouiljsetstatus: new -> open
messages: + msg8217
2024-12-08 16:06:09rouiljsetmessages: + msg8216
2024-12-08 07:36:43rouiljsetmessages: + msg8214
2024-12-08 07:27:23rouiljsetassignee: rouilj
messages: + msg8213
2024-12-08 07:18:00rouiljsetmessages: + msg8212
2023-08-02 21:59:48rouiljsetmessages: + msg7823
2019-10-28 15:16:20schlatterbecksetnosy: + schlatterbeck
2019-10-28 00:29:03rouiljcreate