|
|
|
//
|
|
|
|
// user.go
|
|
|
|
// Copyright (C) 2024 veypi <i@veypi.com>
|
|
|
|
// 2024-10-22 15:49
|
|
|
|
// Distributed under terms of the GPL license.
|
|
|
|
//
|
|
|
|
|
|
|
|
package fs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"encoding/base64"
|
|
|
|
"net/http"
|
|
|
|
"oa/cfg"
|
|
|
|
"oa/errs"
|
|
|
|
"oa/libs/auth"
|
|
|
|
"oa/libs/webdav"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/veypi/utils"
|
|
|
|
"github.com/veypi/utils/logv"
|
|
|
|
)
|
|
|
|
|
|
|
|
func NewUserFs(prefix string) func(http.ResponseWriter, *http.Request) {
|
|
|
|
if strings.HasSuffix(prefix, "/") {
|
|
|
|
prefix = prefix[:len(prefix)-1]
|
|
|
|
}
|
|
|
|
tmp := utils.PathJoin(cfg.Config.FsPath, "u")
|
|
|
|
if !utils.FileExists(tmp) {
|
|
|
|
logv.AssertError(os.MkdirAll(tmp, 0744))
|
|
|
|
}
|
|
|
|
|
|
|
|
client := webdav.NewWebdav(tmp)
|
|
|
|
client.Prefix = prefix
|
|
|
|
client.RootIndex = 4
|
|
|
|
client.Logger = func(r *http.Request, err error) {
|
|
|
|
}
|
|
|
|
client.GenSubPathFunc = func(r *http.Request) (string, error) {
|
|
|
|
// /:aid/*p
|
|
|
|
uid, root := getid(r.URL.Path, prefix)
|
|
|
|
if root == "/" {
|
|
|
|
if !utils.FileExists(tmp + "/" + uid) {
|
|
|
|
os.MkdirAll(tmp+"/"+uid, 0744)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.Method == "OPTIONS" {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
payload, err := getToken(r)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if payload.Access.CheckPrefix("fs", root, auth.Do) && payload.UID == uid {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
return "", errs.AuthNoPerm
|
|
|
|
}
|
|
|
|
return client.ServeHTTP
|
|
|
|
}
|
|
|
|
|
|
|
|
func getid(url, prefix string) (string, string) {
|
|
|
|
if strings.HasSuffix(prefix, "/") {
|
|
|
|
prefix = prefix[:len(prefix)-1]
|
|
|
|
}
|
|
|
|
dir := strings.TrimPrefix(url, prefix)
|
|
|
|
dirs := strings.Split(dir[1:], "/")
|
|
|
|
id := ""
|
|
|
|
root := "/"
|
|
|
|
if len(dirs) > 0 {
|
|
|
|
id = dirs[0]
|
|
|
|
}
|
|
|
|
if len(dirs) > 1 {
|
|
|
|
root = "/" + strings.Join(dirs[1:], "/")
|
|
|
|
}
|
|
|
|
return id, root
|
|
|
|
}
|
|
|
|
|
|
|
|
func getToken(r *http.Request) (*auth.Claims, error) {
|
|
|
|
authHeader := r.Header.Get("Authorization")
|
|
|
|
token := ""
|
|
|
|
if authHeader != "" {
|
|
|
|
typ := ""
|
|
|
|
if tags := strings.Split(authHeader, " "); len(tags) > 1 {
|
|
|
|
typ = strings.ToLower(tags[0])
|
|
|
|
}
|
|
|
|
if typ == "basic" {
|
|
|
|
decodedAuth, err := base64.StdEncoding.DecodeString(authHeader[6:])
|
|
|
|
if err != nil {
|
|
|
|
return nil, errs.AuthInvalid
|
|
|
|
}
|
|
|
|
// 通常认证信息格式为 username:password
|
|
|
|
credentials := string(decodedAuth)
|
|
|
|
reader := bufio.NewReader(strings.NewReader(credentials))
|
|
|
|
username, _ := reader.ReadString(':')
|
|
|
|
password := credentials[len(username):]
|
|
|
|
|
|
|
|
username = strings.TrimSuffix(username, "\n")
|
|
|
|
username = strings.TrimSuffix(username, ":")
|
|
|
|
token = strings.TrimPrefix(password, "\n")
|
|
|
|
} else if typ == "bearer" {
|
|
|
|
token = authHeader[7:]
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
acookie, err := r.Cookie("fstoken")
|
|
|
|
if err == nil {
|
|
|
|
token = acookie.Value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if token == "" {
|
|
|
|
return nil, errs.AuthNotFound
|
|
|
|
}
|
|
|
|
payload, err := auth.ParseJwt(token)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return payload, nil
|
|
|
|
}
|