...
 
Commits (4)
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE uploads (
CREATE TABLE file (
nonce bytea,
objectname uuid PRIMARY KEY,
createdat timestamp,
......
......@@ -70,3 +70,20 @@ func (c chain) then(h http.Handler) http.Handler {
return h
}
// Append extends a chain, adding the specified constructors
// as the last ones in the request flow.
//
// Append returns a new chain, leaving the original one untouched.
//
// stdChain := alice.New(m1, m2)
// extChain := stdChain.Append(m3, m4)
// // requests in stdChain go m1 -> m2
// // requests in extChain go m1 -> m2 -> m3 -> m4
func (c chain) Append(constructors ...constructor) chain {
newCons := make([]constructor, 0, len(c.constructors)+len(constructors))
newCons = append(newCons, c.constructors...)
newCons = append(newCons, constructors...)
return chain{newCons}
}
package http
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
......@@ -19,7 +21,7 @@ import (
// InternalServer is a Handler that responds with
// http.StatusInternalServerError
func InternalServer(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Internal server error", http.StatusInternalServerError)
http.Error(w, "Internal server Error", http.StatusInternalServerError)
return
}
......@@ -95,8 +97,6 @@ func (h receiveFile) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
// Effectivly set a max size on the http body.
r.Body = http.MaxBytesReader(w, r.Body, int64(h.Config.MaxUploadSize))
reader, err := r.MultipartReader()
if err != nil {
log.Print(err)
......@@ -158,7 +158,7 @@ func (h receiveFile) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Printf("Successfully uploaded %s of size %d \n", objectName.String(), n)
}
if _, err := h.DB.Exec("insert into uploads (nonce, objectname, createdat, invalidat) values ($1, $2, $3, $4);", nonce[:], objectName.String(), createdat, invalidat); err != nil {
if _, err := h.DB.Exec("insert into file (nonce, objectname, createdat, invalidat) values ($1, $2, $3, $4);", nonce[:], objectName.String(), createdat, invalidat); err != nil {
log.Printf("Failed to update database: %v", err)
err := h.Minio.RemoveObject(h.Config.BucketName.String(), objectName.String())
if err != nil {
......@@ -205,7 +205,7 @@ func (h getFile) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Print(err)
return
}
err = h.DB.QueryRow("SELECT * FROM uploads WHERE objectname = $1;", userUUID.String()).Scan(&dbNonce, &tmpDbObjectName, &dbCreatedat, &dbInvalidat)
err = h.DB.QueryRow("SELECT * FROM file WHERE objectname = $1;", userUUID.String()).Scan(&dbNonce, &tmpDbObjectName, &dbCreatedat, &dbInvalidat)
if err != nil {
if err.Error() == "no rows in result set" {
http.Error(w, "Not Found", http.StatusNotFound)
......@@ -252,3 +252,118 @@ func (h getFile) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
return
}
type receiveSecret struct {
*app.Context
}
type secret struct {
Value string `json:"value"`
Email string `json:"email"`
Description string `json:"description"`
}
func (h receiveSecret) ServeHTTP(w http.ResponseWriter, r *http.Request) {
paths := strings.Split(r.URL.Path, "/")
if len(paths) != 2 {
http.Error(w, "Not Found", http.StatusNotFound)
return
}
var (
objectName uuid.UUID
nonce [32]byte
createdat time.Time
invalidat time.Time
key [32]byte
secret secret
)
objectName, err := uuid.New()
if err != nil {
log.Print(err)
InternalServer(w, r)
return
}
if _, err = crypto.Nonce(nonce[:]); err != nil {
log.Printf("Failed to read random data: %v", err)
InternalServer(w, r)
return
}
if key, err = crypto.EncryptionKey(nonce[:]); err != nil {
log.Printf("Failed to derive encryption key: %v", err)
InternalServer(w, r)
return
}
if r.Body == nil {
http.Error(w, "A request body is required", 400)
return
}
buf := new(bytes.Buffer)
_, err = buf.ReadFrom(r.Body)
if err != nil {
log.Print(err)
InternalServer(w, r)
return
}
err = json.Unmarshal(buf.Bytes(), &secret)
if err != nil {
log.Print(err)
InternalServer(w, r)
return
}
pReader, pWriter := io.Pipe()
go func() {
defer pWriter.Close()
if _, err = sio.Encrypt(pWriter, buf, sio.Config{Key: key[:]}); err != nil {
log.Printf("Failed to encrypt data: %v", err)
InternalServer(w, r)
return
}
}()
err = h.Minio.MakeBucket(h.Config.BucketName.String(), h.Config.Location)
if err != nil {
exists, err := h.Minio.BucketExists(h.Config.BucketName.String())
if err == nil && exists {
log.Printf("We already own %s\n", h.Config.BucketName.String())
} else {
log.Println(err)
InternalServer(w, r)
return
}
} else {
log.Printf("Successfully created %s\n", h.Config.BucketName.String())
}
n, err := h.Minio.PutObject(h.Config.BucketName.String(), objectName.String(), pReader, -1, minio.PutObjectOptions{})
if err != nil {
log.Println(err)
InternalServer(w, r)
return
}
createdat = time.Now()
invalidat = createdat.Add(time.Second * time.Duration(h.Config.TTL))
log.Printf("Successfully uploaded %s of size %d \n", objectName.String(), n)
if _, err := h.DB.Exec("insert into file (nonce, objectname, createdat, invalidat) values ($1, $2, $3, $4);", nonce[:], objectName.String(), createdat, invalidat); err != nil {
log.Printf("Failed to update database: %v", err)
err := h.Minio.RemoveObject(h.Config.BucketName.String(), objectName.String())
if err != nil {
log.Printf("Failed to clean up object %s: %v", objectName.String(), err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
InternalServer(w, r)
return
}
log.Printf("Database update successfull\n")
w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf("%s\n", objectName.String())))
return
}
......@@ -18,11 +18,14 @@ func Listen(ac *app.Context) error {
checkIP := func(h http.Handler) http.Handler {
return middleware.IPChecker(ac, h)
}
maxBodySize := func(h http.Handler) http.Handler {
return middleware.MaxBodySize(ac, h)
}
notFound := http.NotFoundHandler()
var logChain = newChain(accessLog)
var checkIPChain = newChain(accessLog, checkIP)
http.Handle("/", logChain.then(notFound))
var stdChain = newChain(middleware.AddRequestID, accessLog, maxBodySize)
var checkIPChain = stdChain.Append(checkIP)
http.Handle("/", stdChain.then(notFound))
http.Handle("/file", checkIPChain.then(switchfile{ac}))
http.Handle("/file/", checkIPChain.then(switchfile{ac}))
......
package middleware
import (
"gitlab.com/MadsRC/vee"
"context"
"log"
"net"
"net/http"
"strings"
"gitlab.com/MadsRC/vee"
"gitlab.com/MadsRC/vee/lib/uuid"
)
// customResponseWriter wraps net/http.ResponseWriter and provides a convenient
......@@ -56,12 +59,38 @@ func (mw *customResponseWriter) Status() int {
return mw.status
}
type contextKey string
const ctxKey contextKey = "requestID"
// MaxBodySize enforces a max body size for the client body
func MaxBodySize(ac *app.Context, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, int64(ac.Config.MaxUploadSize))
next.ServeHTTP(w, r)
})
}
// AddRequestID attaches a UUIDv4 to the context of the request
func AddRequestID(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
id, err := uuid.New()
if err != nil {
log.Println(err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
ctx := context.WithValue(context.Background(), ctxKey, id)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// AccessLog ensures that connections are logged.
func AccessLog(ac *app.Context, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rw := &customResponseWriter{w: w, status: -1, size: 0}
next.ServeHTTP(rw, r)
log.Printf("%s %s %v %d %d", strings.Split(r.RemoteAddr, ":")[0], r.Method, r.URL, rw.status, rw.size)
log.Printf("%s (%s) %s %v %d %d", strings.Split(r.RemoteAddr, ":")[0], r.Context().Value(ctxKey), r.Method, r.URL, rw.status, rw.size)
})
}
......