Files
filestash/server/ctrl/share.go
2025-10-09 12:32:01 +11:00

214 lines
5.7 KiB
Go

package ctrl
import (
"encoding/json"
"fmt"
"github.com/gorilla/mux"
. "github.com/mickael-kerjean/filestash/server/common"
"github.com/mickael-kerjean/filestash/server/model"
"net/http"
"strings"
)
func ShareList(ctx *App, res http.ResponseWriter, req *http.Request) {
path, err := PathBuilder(ctx, req.URL.Query().Get("path"))
if err != nil {
SendErrorResult(res, err)
return
}
listOfSharedLinks, err := model.ShareList(
GenerateID(ctx.Session),
path,
)
if err != nil {
Log.Debug("share::list '%s'", err.Error())
SendErrorResult(res, err)
return
}
for i := 0; i < len(listOfSharedLinks); i++ {
listOfSharedLinks[i].Path = "/" + strings.TrimPrefix(listOfSharedLinks[i].Path, path)
}
SendSuccessResults(res, listOfSharedLinks)
}
func ShareUpsert(ctx *App, res http.ResponseWriter, req *http.Request) {
share_id := mux.Vars(req)["share"]
if share_id == "private" {
Log.Debug("share::upsert 'private'")
SendErrorResult(res, ErrNotValid)
return
}
s := Share{
Id: share_id,
Auth: func() string {
if ctx.Share.Id == "" {
str := ""
index := 0
for {
cookie, err := req.Cookie(CookieName(index))
if err != nil {
break
}
index++
str += cookie.Value
}
return str
}
return ctx.Share.Auth
}(),
Backend: func() string {
if ctx.Share.Id == "" {
return GenerateID(ctx.Session)
}
return ctx.Share.Backend
}(),
Path: func() string {
leftPath := "/"
rightPath := strings.TrimPrefix(NewStringFromInterface(ctx.Body["path"]), "/")
if ctx.Share.Id != "" {
leftPath = ctx.Share.Path
} else {
if ctx.Session["path"] != "" {
leftPath = EnforceDirectory(ctx.Session["path"])
}
}
return leftPath + rightPath
}(),
Password: NewStringpFromInterface(ctx.Body["password"]),
Users: NewStringpFromInterface(ctx.Body["users"]),
Expire: NewInt64pFromInterface(ctx.Body["expire"]),
Url: NewStringpFromInterface(ctx.Body["url"]),
CanManageOwn: NewBoolFromInterface(ctx.Body["can_manage_own"]),
CanShare: NewBoolFromInterface(ctx.Body["can_share"]),
CanRead: NewBoolFromInterface(ctx.Body["can_read"]),
CanWrite: NewBoolFromInterface(ctx.Body["can_write"]),
CanUpload: NewBoolFromInterface(ctx.Body["can_upload"]),
}
if err := model.ShareUpsert(&s); err != nil {
Log.Debug("share::upsert '%s'", err.Error())
SendErrorResult(res, err)
return
}
SendSuccessResult(res, nil)
}
func ShareDelete(ctx *App, res http.ResponseWriter, req *http.Request) {
share_target := mux.Vars(req)["share"]
if err := model.ShareDelete(share_target); err != nil {
Log.Debug("share::delete '%s'", err.Error())
SendErrorResult(res, err)
return
}
SendSuccessResult(res, nil)
}
func ShareVerifyProof(ctx *App, res http.ResponseWriter, req *http.Request) {
var submittedProof model.Proof
var verifiedProof []model.Proof
var requiredProof []model.Proof
var remainingProof []model.Proof
var s Share
var err error
// 1) initialise the current context
share_id := mux.Vars(req)["share"]
s, err = model.ShareGet(share_id)
if err != nil {
Log.Debug("share::verify::init '%s'", err.Error())
SendErrorResult(res, err)
return
}
submittedProof = model.Proof{
Key: fmt.Sprint(ctx.Body["type"]),
Value: fmt.Sprint(ctx.Body["value"]),
}
verifiedProof = model.ShareProofGetAlreadyVerified(req)
requiredProof = model.ShareProofGetRequired(s)
// 2) validate the current context
if len(verifiedProof) > 20 || len(requiredProof) > 20 {
http.SetCookie(res, &http.Cookie{
Name: COOKIE_NAME_PROOF,
Value: "",
MaxAge: -1,
Path: COOKIE_PATH,
})
Log.Debug("share::verify::validate 'proof issue' len(verifiedProof)[%d] len(requiredProof)[%d]", len(verifiedProof), len(requiredProof))
SendErrorResult(res, ErrNotValid)
return
}
if err := s.IsValid(); err != nil {
Log.Debug("share::verify::validate '%s'", err.Error())
SendErrorResult(res, err)
return
}
// 3) process the proof sent by the user
submittedProof, err = model.ShareProofVerifier(s, submittedProof)
if err != nil {
Log.Debug("share::verify::process '%s'", err.Error())
submittedProof.Error = NewString(err.Error())
SendSuccessResult(res, submittedProof)
return
}
if submittedProof.Key == "code" {
submittedProof.Value = ""
submittedProof.Message = NewString("We've sent you a message with a verification code")
SendSuccessResult(res, submittedProof)
return
}
if submittedProof.Key != "" {
submittedProof.Id = Hash(submittedProof.Key+"::"+submittedProof.Value, 20)
alreadyExist := false
for i := 0; i < len(verifiedProof); i++ {
if verifiedProof[i].Id == submittedProof.Id {
alreadyExist = true
break
}
}
if alreadyExist == false {
verifiedProof = append(verifiedProof, submittedProof)
}
}
// 4) Find remaining proofs: requiredProof - verifiedProof
remainingProof = model.ShareProofCalculateRemainings(requiredProof, verifiedProof)
// 5) persist proofs in client cookie
cookie := http.Cookie{
Name: COOKIE_NAME_PROOF,
Value: func(p []model.Proof) string {
j, _ := json.Marshal(p)
str, _ := EncryptString(SECRET_KEY_DERIVATE_FOR_PROOF, string(j))
return str
}(verifiedProof),
Path: COOKIE_PATH,
MaxAge: 60 * 60 * 24 * 30,
HttpOnly: true,
SameSite: http.SameSiteNoneMode,
Secure: true,
}
http.SetCookie(res, &cookie)
if len(remainingProof) > 0 {
SendSuccessResult(res, remainingProof[0])
return
}
SendSuccessResult(res, struct {
Id string `json:"id"`
Path string `json:"path"`
CanRead bool `json:"can_read"`
CanWrite bool `json:"can_write"`
CanUpload bool `json:"can_upload"`
}{
Id: s.Id,
Path: s.Path,
CanRead: s.CanRead,
CanWrite: s.CanWrite,
CanUpload: s.CanUpload,
})
}