diff --git a/cmd/tusd/cli/hooks.go b/cmd/tusd/cli/hooks.go index 1bb9210..27ea5e6 100644 --- a/cmd/tusd/cli/hooks.go +++ b/cmd/tusd/cli/hooks.go @@ -43,6 +43,10 @@ func preFinishCallback(info handler.HookEvent) error { return hookCallback(hooks.HookPreFinish, info) } +func preGetCallback(info handler.HookEvent) error { + return hookCallback(hooks.HookPreGet, info) +} + func SetupHookMetrics() { MetricsHookErrorsTotal.WithLabelValues(string(hooks.HookPostFinish)).Add(0) MetricsHookErrorsTotal.WithLabelValues(string(hooks.HookPostTerminate)).Add(0) @@ -50,6 +54,7 @@ func SetupHookMetrics() { MetricsHookErrorsTotal.WithLabelValues(string(hooks.HookPostCreate)).Add(0) MetricsHookErrorsTotal.WithLabelValues(string(hooks.HookPreCreate)).Add(0) MetricsHookErrorsTotal.WithLabelValues(string(hooks.HookPreFinish)).Add(0) + MetricsHookErrorsTotal.WithLabelValues(string(hooks.HookPreGet)).Add(0) } func SetupPreHooks(config *handler.Config) error { @@ -99,6 +104,7 @@ func SetupPreHooks(config *handler.Config) error { config.PreUploadCreateCallback = preCreateCallback config.PreFinishResponseCallback = preFinishCallback + config.PreGetResponseCallback = preGetCallback return nil } @@ -115,6 +121,8 @@ func SetupPostHooks(handler *handler.Handler) { invokeHookAsync(hooks.HookPostReceive, info) case info := <-handler.CreatedUploads: invokeHookAsync(hooks.HookPostCreate, info) + case info := <-handler.GetUploads: + invokeHookAsync(hooks.HookPreGet, info) } } }() diff --git a/cmd/tusd/cli/hooks/hooks.go b/cmd/tusd/cli/hooks/hooks.go index b72e3c3..be1a0e4 100644 --- a/cmd/tusd/cli/hooks/hooks.go +++ b/cmd/tusd/cli/hooks/hooks.go @@ -18,9 +18,10 @@ const ( HookPostCreate HookType = "post-create" HookPreCreate HookType = "pre-create" HookPreFinish HookType = "pre-finish" + HookPreGet HookType = "pre-get" ) -var AvailableHooks []HookType = []HookType{HookPreCreate, HookPostCreate, HookPostReceive, HookPostTerminate, HookPostFinish, HookPreFinish} +var AvailableHooks []HookType = []HookType{HookPreCreate, HookPostCreate, HookPostReceive, HookPostTerminate, HookPostFinish, HookPreFinish, HookPreGet} type hookDataStore struct { handler.DataStore diff --git a/cmd/tusd/cli/hooks/plugin.go b/cmd/tusd/cli/hooks/plugin.go index 8821527..9794d72 100644 --- a/cmd/tusd/cli/hooks/plugin.go +++ b/cmd/tusd/cli/hooks/plugin.go @@ -14,6 +14,7 @@ type PluginHookHandler interface { PostFinish(info handler.HookEvent) error PostTerminate(info handler.HookEvent) error PreFinish(info handler.HookEvent) error + PreGet(info handler.HookEvent) error } type PluginHook struct { @@ -57,6 +58,8 @@ func (h PluginHook) InvokeHook(typ HookType, info handler.HookEvent, captureOutp err = h.handler.PreCreate(info) case HookPreFinish: err = h.handler.PreFinish(info) + case HookPreGet: + err = h.handler.PreGet(info) default: err = fmt.Errorf("hooks: unknown hook named %s", typ) } diff --git a/docs/hooks.md b/docs/hooks.md index 2bdd6c6..91660f4 100644 --- a/docs/hooks.md +++ b/docs/hooks.md @@ -13,7 +13,7 @@ If not otherwise noted, all hooks are invoked in a *non-blocking* way, meaning t ## Blocking Hooks -On the other hand, there are a few *blocking* hooks, such as caused by the `pre-create` and `pre-finish` events. Because their exit code will dictate whether tusd will accept the current incoming request, tusd will wait until the hook process has exited. Therefore, in order to keep the response times low, one should avoid to make time-consuming operations inside the processes for blocking hooks. +On the other hand, there are a few *blocking* hooks, such as caused by the `pre-create`, `pre-get` and `pre-finish` events. Because their exit code will dictate whether tusd will accept the current incoming request, tusd will wait until the hook process has exited. Therefore, in order to keep the response times low, one should avoid to make time-consuming operations inside the processes for blocking hooks. ### Blocking File Hooks @@ -39,6 +39,12 @@ This event will be triggered after an upload is fully finished but before a resp This is a blocking hook, as such it can be used to validate or post-process an uploaded file. A non-zero exit code or HTTP response greater than `400` will return a HTTP 500 error to the client. +### pre-get + +This event will be triggered before an upload read returned to the client. +This is a blocking hook, as such it can be used to validate access limits. +A non-zero exit code or HTTP response greater than `400` will return a HTTP 500 error to the client. + ### post-finish This event will be triggered after an upload is fully finished, meaning that all chunks have been transfered and saved in the storage. After this point, no further modifications, except possible deletion, can be made to the upload entity and it may be desirable to use the file for further processing or notify other applications of the completions of this upload. diff --git a/examples/hooks/pre-get b/examples/hooks/pre-get new file mode 100644 index 0000000..1d822ae --- /dev/null +++ b/examples/hooks/pre-get @@ -0,0 +1,7 @@ +#!/bin/sh + +filename=$(cat /dev/stdin | jq .MetaData.filename) +if [[ $filename != "public" ]; then + echo "Error: access unauthorized" + exit 1 +fi \ No newline at end of file diff --git a/pkg/handler/config.go b/pkg/handler/config.go index ae9676b..83a4df6 100644 --- a/pkg/handler/config.go +++ b/pkg/handler/config.go @@ -40,6 +40,11 @@ type Config struct { // potentially set by proxies when generating an absolute URL in the // response to POST requests. RespectForwardedHeaders bool + // PreGetCallback will be invoked before an upload is read, if the + // property is supplied. If the callback returns nil, the upload will be sent. + // Otherwise the HTTP request will be aborted. This can be used to implement + // accesss limits. + PreGetCallback func(hook HookEvent) error // PreUploadCreateCallback will be invoked before a new upload is created, if the // property is supplied. If the callback returns nil, the upload will be created. // Otherwise the HTTP request will be aborted. This can be used to implement diff --git a/pkg/handler/unrouted_handler.go b/pkg/handler/unrouted_handler.go index 6c5f52b..88d4d11 100644 --- a/pkg/handler/unrouted_handler.go +++ b/pkg/handler/unrouted_handler.go @@ -741,6 +741,13 @@ func (handler *UnroutedHandler) GetFile(w http.ResponseWriter, r *http.Request) return } + if handler.config.PreGetCallback != nil { + if err := handler.config.PreGetCallback(newHookEvent(info, r)); err != nil { + handler.sendError(w, r, err) + return + } + } + // Set headers before sending responses w.Header().Set("Content-Length", strconv.FormatInt(info.Offset, 10))