Initial commit
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"send/internal/auth"
|
||||
"send/internal/store"
|
||||
)
|
||||
|
||||
// handleDelete removes an object early. Three credentials are accepted: the
|
||||
// delete token handed to the uploader, the token that owns the object, and any
|
||||
// admin token.
|
||||
//
|
||||
// There is only one delete route, and it is a POST, so the success page's plain
|
||||
// form works with scripting disabled and no second code path is needed.
|
||||
func (s *Server) handleDelete(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := store.CleanID(r.PathValue("id"))
|
||||
if err != nil {
|
||||
s.fail(w, r, http.StatusNotFound, "No such file.")
|
||||
return
|
||||
}
|
||||
m, err := s.store.Get(id, s.now())
|
||||
if err != nil {
|
||||
s.fail(w, r, http.StatusNotFound, "No such file.")
|
||||
return
|
||||
}
|
||||
|
||||
secret := bearer(r)
|
||||
if secret == "" {
|
||||
// A small form post; the 4 KiB cap keeps this from being a way to
|
||||
// stream a body into memory.
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxFieldBytes)
|
||||
if err := r.ParseForm(); err == nil {
|
||||
secret = strings.TrimSpace(r.PostFormValue("token"))
|
||||
}
|
||||
}
|
||||
if secret == "" {
|
||||
s.fail(w, r, http.StatusUnauthorized, "A delete token or an owning token is required.")
|
||||
return
|
||||
}
|
||||
|
||||
if !s.mayDelete(m, secret) {
|
||||
s.fail(w, r, http.StatusForbidden, "That token cannot delete this file.")
|
||||
return
|
||||
}
|
||||
if err := s.store.Delete(id); err != nil {
|
||||
s.log.Error("deleting object", "id", id, "err", err)
|
||||
s.fail(w, r, http.StatusInternalServerError, "Could not delete the file.")
|
||||
return
|
||||
}
|
||||
s.log.Info("deleted", "id", id, "ip", clientIP(r, s.cfg))
|
||||
|
||||
if wantsJSON(r) {
|
||||
writeJSON(w, http.StatusOK, map[string]string{"status": "deleted", "id": id})
|
||||
return
|
||||
}
|
||||
s.render(w, http.StatusOK, "error.html", errorPage{
|
||||
page: s.page("Deleted", false),
|
||||
Status: "Deleted",
|
||||
Message: "The file is gone.",
|
||||
})
|
||||
}
|
||||
|
||||
// mayDelete checks the presented secret against the object's delete token
|
||||
// first, then against the token file.
|
||||
func (s *Server) mayDelete(m *store.Meta, secret string) bool {
|
||||
if auth.EqualHash(m.DeleteHash, auth.HashSecret(secret)) {
|
||||
return true
|
||||
}
|
||||
if err := s.tokens.MaybeReload(); err != nil {
|
||||
s.log.Error("reloading token file", "err", err)
|
||||
}
|
||||
t := s.tokens.Lookup(secret)
|
||||
if t == nil {
|
||||
return false
|
||||
}
|
||||
return t.Admin || (m.Owner != "" && t.Name == m.Owner)
|
||||
}
|
||||
Reference in New Issue
Block a user