validatechecksum.go (124 lines of code) (raw):
package main
import (
"crypto/sha1"
"encoding/base64"
"fmt"
"github.com/go-redis/redis/v8"
"github.com/google/uuid"
"gitlab.com/codmill/customer-projects/guardian/deliverable-receiver/helpers"
"gitlab.com/codmill/customer-projects/guardian/deliverable-receiver/models"
"io"
"log"
"net/http"
"os"
)
type ValidateChecksumHandler struct {
redisClient *redis.Client
config *helpers.Config
}
func GetLocalSHA(filename string) (string, error) {
f, openErr := os.Open(filename)
if openErr != nil {
log.Printf("ERROR GetLocalSHA could not open %s: %s", filename, openErr)
return "", openErr
}
defer f.Close()
h := sha1.New()
if _, err := io.Copy(h, f); err != nil {
log.Printf("ERROR GetLocalSHA could not read local file %s: %s", filename, err)
return "", err
}
return base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
}
func (h ValidateChecksumHandler) ServeHTTP(w http.ResponseWriter, request *http.Request) {
if !helpers.AssertHttpMethod(request, w, "GET") {
return
}
username, validationErr := helpers.ValidateLogin(request, h.config)
if validationErr != nil {
log.Printf("ERROR ValidateChecksumHandler could not validate request: %s", validationErr)
response := helpers.GenericErrorResponse{
Status: "forbidden",
Detail: validationErr.Error(),
}
helpers.WriteJsonContent(response, w, 403)
return
}
params, getParamsErr := helpers.GetQueryParams(request.RequestURI)
if getParamsErr != nil {
log.Printf("ERROR ValidateChecksumHandler could not get request params from '%s': %s", request.RequestURI, getParamsErr)
response := helpers.GenericErrorResponse{
Status: "bad_request",
Detail: "invalid request params",
}
helpers.WriteJsonContent(response, w, 400)
return
}
fileName := params.Get("fileName")
uploadId := params.Get("uploadId")
clientChecksum := params.Get("sum")
if fileName == "" || uploadId == "" || clientChecksum == "" {
response := helpers.GenericErrorResponse{
Status: "bad_request",
Detail: "You must provide fileName, uploadId and sum as query parameters",
}
helpers.WriteJsonContent(response, w, 400)
return
}
uploadSlotUuid, uuidErr := uuid.Parse(uploadId)
if uuidErr != nil {
response := helpers.GenericErrorResponse{
Status: "invalid_request",
Detail: "uploadId must be a uuid",
}
helpers.WriteJsonContent(response, w, 400)
return
}
uploadSlot, slotErr := models.UploadSlotForId(uploadSlotUuid, h.redisClient)
if slotErr != nil {
log.Printf("ERROR ValidateChecksumHandler could not get upload slot for '%s': %s", uploadSlotUuid, slotErr)
response := helpers.GenericErrorResponse{
Status: "db_error",
Detail: "could not get upload slot",
}
helpers.WriteJsonContent(response, w, 500)
return
}
if uploadSlot == nil {
log.Printf("WARNING ValidateChecksumHandler no upload slot for '%s', this might just mean it's expire", uploadSlotUuid)
response := helpers.GenericErrorResponse{
Status: "not_found",
Detail: "upload slot does not exist",
}
helpers.WriteJsonContent(response, w, 404)
return
}
//get a sanitised, absolute path to write the file
targetFilename := getTargetFilename(h.config.StoragePrefix.LocalPath, uploadSlot.UploadPathRelative, fileName)
log.Printf("INFO ValidateChecksumHandler checksum request from %s to %s", username, targetFilename)
sha, shaErr := GetLocalSHA(targetFilename)
if shaErr != nil {
log.Printf("ERROR ValidateChecksumHandler could not calculate local SHA: %s", shaErr)
response := helpers.GenericErrorResponse{
Status: "error",
Detail: shaErr.Error(),
}
helpers.WriteJsonContent(response, w, 500)
return
}
log.Printf("INFO ValidateChecksumHandler local SHA is %s, client SHA is %s", sha, clientChecksum)
if sha == clientChecksum {
response := helpers.GenericErrorResponse{
Status: "ok",
Detail: "checksums matched",
}
helpers.WriteJsonContent(response, w, 200)
return
} else {
response := helpers.GenericErrorResponse{
Status: "conflict",
Detail: fmt.Sprintf("local sha is %s but client is %s", sha, clientChecksum),
}
helpers.WriteJsonContent(response, w, 409)
return
}
}