You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/oa/builtin/fs/user.go

121 lines
2.6 KiB
Go

//
// 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
}