streamUpload handles streaming file uploads. The encrypted binary data is streamed directly to the FileStore while metadata is stored in the Database.
(w http.ResponseWriter, r *http.Request)
| 22 | // The encrypted binary data is streamed directly to the FileStore |
| 23 | // while metadata is stored in the Database. |
| 24 | func (y *Server) streamUpload(w http.ResponseWriter, r *http.Request) { |
| 25 | session, _ := y.getSession(r) |
| 26 | audit := y.newAuditor("file.uploaded", y.getRealClientIP(r), session) |
| 27 | |
| 28 | mediaType, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type")) |
| 29 | if mediaType != "application/octet-stream" { |
| 30 | audit.failure("invalid content-type") |
| 31 | jsonError(w, http.StatusBadRequest, "Content-Type must be application/octet-stream") |
| 32 | return |
| 33 | } |
| 34 | |
| 35 | // Parse headers |
| 36 | expirationStr := r.Header.Get("X-Yopass-Expiration") |
| 37 | if expirationStr == "" { |
| 38 | audit.failure("missing expiration header") |
| 39 | jsonError(w, http.StatusBadRequest, "X-Yopass-Expiration header required") |
| 40 | return |
| 41 | } |
| 42 | expiration, err := strconv.ParseInt(expirationStr, 10, 32) |
| 43 | if err != nil || !validExpiration(int32(expiration)) { |
| 44 | audit.failure("invalid expiration") |
| 45 | jsonError(w, http.StatusBadRequest, "Invalid expiration specified") |
| 46 | return |
| 47 | } |
| 48 | |
| 49 | if y.ForceExpiration != "" { |
| 50 | forced := expirationInSeconds(y.ForceExpiration) |
| 51 | if int32(expiration) != forced { |
| 52 | audit.failure("expiration does not match forced value") |
| 53 | jsonError(w, http.StatusBadRequest, "Expiration does not match server policy") |
| 54 | return |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | oneTime := r.Header.Get("X-Yopass-OneTime") == "true" |
| 59 | if !oneTime && y.ForceOneTimeSecrets { |
| 60 | audit.failure("one-time required by server policy") |
| 61 | jsonError(w, http.StatusBadRequest, "Secret must be one time download") |
| 62 | return |
| 63 | } |
| 64 | |
| 65 | requireAuth := r.Header.Get("X-Yopass-RequireAuth") == "true" |
| 66 | if requireAuth && y.OIDCProvider == nil { |
| 67 | audit.failure("auth required but OIDC not configured") |
| 68 | jsonError(w, http.StatusBadRequest, "Authentication not configured on this server") |
| 69 | return |
| 70 | } |
| 71 | |
| 72 | receipt := r.Header.Get("X-Yopass-Receipt") == "true" |
| 73 | if receipt && !y.readReceiptsEnabled() { |
| 74 | audit.failure("read receipts not enabled") |
| 75 | jsonError(w, http.StatusBadRequest, "Read receipts are not enabled on this server") |
| 76 | return |
| 77 | } |
| 78 | |
| 79 | // Reject early if Content-Length exceeds limit |
| 80 | if y.MaxFileSize > 0 && r.ContentLength > y.MaxFileSize { |
| 81 | audit.failure("file too large") |