/
upload.go
138 lines (119 loc) · 3.79 KB
/
upload.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package middleware
import (
"fmt"
"net/http"
"strings"
"github.com/gorilla/mux"
"github.com/root-gg/utils"
"github.com/root-gg/plik/server/context"
)
// Upload retrieve the requested upload metadata from the metadataBackend and save it to the request context.
func Upload(ctx *context.Context, next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
log := ctx.GetLogger()
// Get the upload id from the url params
vars := mux.Vars(req)
uploadID := vars["uploadID"]
if uploadID == "" {
ctx.MissingParameter("upload id")
return
}
// Get upload metadata
upload, err := ctx.GetMetadataBackend().GetUpload(uploadID)
if err != nil {
ctx.InternalServerError("unable to get upload metadata", err)
return
}
if upload == nil {
ctx.NotFound("upload %s not found", uploadID)
return
}
// Update request logger prefix
prefix := fmt.Sprintf("%s[%s]", log.Prefix, uploadID)
log.SetPrefix(prefix)
// Test if upload is not expired
if upload.IsExpired() {
ctx.NotFound("upload %s has expired", uploadID)
return
}
// Save upload in the request context
ctx.SetUpload(upload)
// Being admin of an upload means that you can :
// - Add files to the upload
// - Remove the upload
// There are several ways to be considered admin of an upload
// - Providing the correct UploadToken (authenticated or not)
// - Being authenticated with an Admin user
// - Being authenticated with a cookie with the user having created the upload
// - Being authenticated with a token with the user and token having create the upload
upload.IsAdmin = false
uploadToken := req.Header.Get("X-UploadToken")
if uploadToken != "" && uploadToken == upload.UploadToken {
upload.IsAdmin = true
} else {
token := ctx.GetToken()
if token != nil {
// A user authenticated with a token can manage uploads created with such token
if upload.Token == token.Token {
upload.IsAdmin = true
}
} else {
// Check if upload belongs to user or if user is admin
if ctx.IsAdmin() {
upload.IsAdmin = true
} else {
user := ctx.GetUser()
if user != nil && upload.User == ctx.GetUser().ID {
upload.IsAdmin = true
}
}
}
}
forbidden := func(message string) {
resp.Header().Set("WWW-Authenticate", "Basic realm=\"plik\"")
message = fmt.Sprintf("please provide valid credentials to access this upload : %s", message)
// Shouldn't redirect here to let the browser ask for credentials and retry
ctx.SetRedirectOnFailure(false)
ctx.Fail(message, nil, http.StatusUnauthorized)
}
// Handle basic auth if upload is password protected
if upload.ProtectedByPassword && !upload.IsAdmin {
if req.Header.Get("Authorization") == "" {
forbidden("missing Authorization header")
return
}
// Basic auth Authorization header must be set to
// "Basic base64("login:password")". Only the md5sum
// of the base64 string is saved in the upload metadata
auth := strings.Split(req.Header.Get("Authorization"), " ")
if len(auth) != 2 {
forbidden("invalid Authorization header")
return
}
if auth[0] != "Basic" {
forbidden("invalid http authorization scheme")
return
}
var md5sum string
md5sum, err = utils.Md5sum(auth[1])
if err != nil {
forbidden("unable to hash credentials")
return
}
if md5sum != upload.Password {
forbidden("invalid credentials")
return
}
}
// Extend upload expiration date by TTL each time an upload is directly accessed
if upload.ExtendTTL {
upload.ExtendExpirationDate()
err := ctx.GetMetadataBackend().UpdateUploadExpirationDate(upload)
if err != nil {
ctx.InternalServerError("unable to extend upload expiration date", err)
return
}
}
next.ServeHTTP(resp, req)
})
}