Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hooks: add pre-access blocking hook #1077

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ This error can occur when you are running tusd's disk storage on a file system w

### How can I prevent users from downloading the uploaded files?

tusd allows any user to retrieve a previously uploaded file by issuing a HTTP GET request to the corresponding upload URL. This is possible as long as the uploaded files on the datastore have not been deleted or moved to another location. While it is a handy feature for debugging and testing your setup, we know that there are situations where you don't want to allow downloads or where you want more control about who downloads what. In these scenarios we recommend to place a proxy in front of tusd which takes on the task of access control or even preventing HTTP GET requests entirely. tusd has no feature built in for controling or disabling downloads on its own because the main focus is on accepting uploads, not serving files.
tusd allows any user to retrieve a previously uploaded file by issuing a HTTP GET request to the corresponding upload URL. This is possible as long as the uploaded files on the datastore have not been deleted or moved to another location. While it is a handy feature for debugging and testing your setup, we know that there are situations where you don't want to allow downloads or where you want more control about who downloads what. In these scenarios you can use `-disable-download` option or a `pre-access` hook to control access to uploaded files based on http request headers. You can also place a proxy in front of tusd which takes on the task of access control or even preventing HTTP GET requests entirely.

### How can I keep the original filename for the uploads?

Expand Down
23 changes: 21 additions & 2 deletions docs/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ The table below provides an overview of all available hooks.
| pre-finish | Yes | after all upload data has been received but before a response is sent. | sending custom data when an upload is finished | Yes |
| post-finish | No | after all upload data has been received and after a response is sent. | post-processing of upload, logging of upload end | Yes |
| post-terminate | No | after an upload has been terminated. | clean up of allocated resources | Yes |
| pre-access | Yes | before an existing upload is access (Head/Get/Patch/Delete/Upload-Concat). | validation of user authentication | No |

Users should be aware of following things:
- If a hook is _blocking_, tusd will wait with further processing until the hook is completed. This is useful for validation and authentication, where further processing should be stopped if the hook determines to do so. However, long execution time may impact the user experience because the upload processing is blocked while the hook executes.
Expand Down Expand Up @@ -104,6 +105,17 @@ Below you can find an annotated, JSON-ish encoded example of a hook request:
]
// and more ...
}
},

// Information about the files that are begin accessed, to check authorization for instance
"Access": {
// read (Head/Get/Upload-Concat) or write (Patch/Delete)
"Mode": "read"
// All files info that will be access by http request
// Use an array because of Upload-Concat that may target several files
"Uploads": [
// same as Upload
]
}
}
}
Expand Down Expand Up @@ -169,6 +181,11 @@ Below you can find an annotated, JSON-ish encoded example of a hook response:
// it is ignored. Use the HTTPResponse field to send details about the stop
// to the client.
"StopUpload": true

// In case of pre-access or pre-create (when Upload-Concat), reject access to uploads.
// When true, http request will end with 403 status code by default, changeable with
// HTTPResponse override
"RejectAccess": false,
}
```

Expand Down Expand Up @@ -303,7 +320,7 @@ For example, assume that every upload must belong to a specific user project. Th

### Authenticating Users

User authentication can be achieved by two ways: Either, user tokens can be included in the upload meta data, as described in the above example. Alternatively, traditional header fields, such as `Authorization` or `Cookie` can be used to carry user-identifying information. These header values are also present for the hook requests and are accessible for the `pre-create` hook, where the authorization tokens or cookies can be validated to authenticate the user.
User authentication can be achieved by two ways: Either, user tokens can be included in the upload meta data, as described in the above example. Alternatively, traditional header fields, such as `Authorization` or `Cookie` can be used to carry user-identifying information. These header values are also present for the hook requests and are accessible for the `pre-access` hook, where the authorization tokens or cookies can be validated to authenticate the user.

If the authentication is successful, the hook can return an empty hook response to indicate tusd that the upload should continue as normal. If the authentication fails, the hook can instruct tusd to reject the upload and return a custom error response to the client. For example, this is a possible hook response:

Expand All @@ -321,7 +338,9 @@ If the authentication is successful, the hook can return an empty hook response
}
```

Note that this handles authentication during the initial POST request when creating an upload. When tusd responds, it sends a random upload URL to the client, which is used to transmit the remaining data via PATCH and resume the upload via HEAD requests. Currently, there is no mechanism to ensure that the upload is resumed by the same user that created it. We plan on addressing this in the future. However, since the upload URL is randomly generated and only short-lived, it is hard to guess for uninvolved parties.
Note that listen `pre-create` hook only handles authentication during the initial POST request when creating an upload. When tusd responds, it sends a random upload URL to the client, which is used to transmit the remaining data via PATCH and resume the upload via HEAD requests. Since the upload URL is randomly generated and only short-lived, it is hard to guess for uninvolved parties.

To have full protection, consider adding `pre-access` hook too.

### Interrupting Uploads

Expand Down
5 changes: 5 additions & 0 deletions examples/hooks/file/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Run file hook exemple

tusd -hooks-dir=./ -hooks-enabled-events=pre-create,pre-finish,pre-access,post-create,post-receive,post-terminate,post-finish

Adapt enabled-events hooks list for your needs.
10 changes: 10 additions & 0 deletions examples/hooks/file/pre-access
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/sh

# This example demonstrates how to read the hook event details
# from stdin, and output debug messages.

# We use >&2 to write debugging output to stderr. tusd
# will forward these to its stderr. Any output from the
# hook on stdout will be captured by tusd and interpreted
# as a response.
cat /dev/stdin | jq . >&2
4 changes: 2 additions & 2 deletions examples/hooks/grpc/Makefile
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
hook_pb2.py: ../../../cmd/tusd/cli/hooks/proto/v2/hook.proto
python3 -m grpc_tools.protoc --proto_path=../../../cmd/tusd/cli/hooks/proto/v2/ hook.proto --python_out=. --grpc_python_out=.
hook_pb2.py: ../../../pkg/hooks/grpc/proto/hook.proto
python3 -m grpc_tools.protoc --proto_path=../../../pkg/hooks/grpc/proto/ hook.proto --python_out=. --grpc_python_out=.
7 changes: 7 additions & 0 deletions examples/hooks/grpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Run grpc hook exemple

python3 server.py

tusd -hooks-grpc=localhost:8000 -hooks-enabled-events=pre-create,pre-finish,pre-access,post-create,post-receive,post-terminate,post-finish

Adapt enabled-events hooks list for your needs.
Loading