mirror of https://github.com/Xhofe/alist
feat: sign of file
parent
5dbf5db4ff
commit
d89ec89d51
|
@ -88,6 +88,7 @@ func initialSettings() {
|
||||||
{Key: "global_readme", Value: "This is global readme", Type: conf.TypeText, Group: model.GLOBAL},
|
{Key: "global_readme", Value: "This is global readme", Type: conf.TypeText, Group: model.GLOBAL},
|
||||||
{Key: "customize_head", Type: conf.TypeText, Group: model.GLOBAL, Flag: model.PRIVATE},
|
{Key: "customize_head", Type: conf.TypeText, Group: model.GLOBAL, Flag: model.PRIVATE},
|
||||||
{Key: "customize_body", Type: conf.TypeText, Group: model.GLOBAL, Flag: model.PRIVATE},
|
{Key: "customize_body", Type: conf.TypeText, Group: model.GLOBAL, Flag: model.PRIVATE},
|
||||||
|
{Key: "link_expiration", Value: "4", Type: conf.TypeNumber, Group: model.GLOBAL, Flag: model.PRIVATE},
|
||||||
// single settings
|
// single settings
|
||||||
{Key: "token", Value: token, Type: conf.TypeString, Group: model.SINGLE, Flag: model.PRIVATE},
|
{Key: "token", Value: token, Type: conf.TypeString, Group: model.SINGLE, Flag: model.PRIVATE},
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/alist-org/alist/v3/internal/setting"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/sign"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var once sync.Once
|
||||||
|
var instance sign.Sign
|
||||||
|
|
||||||
|
func WithDuration(data string, d time.Duration) string {
|
||||||
|
once.Do(Instance)
|
||||||
|
return instance.Sign(data, time.Now().Add(d).Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
func NotExpired(data string) string {
|
||||||
|
once.Do(Instance)
|
||||||
|
return instance.Sign(data, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Verify(data string, sign string) error {
|
||||||
|
once.Do(Instance)
|
||||||
|
return instance.Verify(data, sign)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Instance() {
|
||||||
|
instance = sign.NewHMACSign([]byte(setting.GetByKey("token")))
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HMACSign struct {
|
||||||
|
SecretKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HMACSign) Sign(data string, expire int64) string {
|
||||||
|
h := hmac.New(sha256.New, s.SecretKey)
|
||||||
|
expireTimeStamp := strconv.FormatInt(expire, 10)
|
||||||
|
_, err := io.WriteString(h, data+":"+expireTimeStamp)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64.URLEncoding.EncodeToString(h.Sum(nil)) + ":" + expireTimeStamp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HMACSign) Verify(data, sign string) error {
|
||||||
|
signSlice := strings.Split(sign, ":")
|
||||||
|
// check whether contains expire time
|
||||||
|
if signSlice[len(signSlice)-1] == "" {
|
||||||
|
return ErrExpireMissing
|
||||||
|
}
|
||||||
|
// check whether expire time is expired
|
||||||
|
expires, err := strconv.ParseInt(signSlice[len(signSlice)-1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return ErrExpireInvalid
|
||||||
|
}
|
||||||
|
// if expire time is expired, return error
|
||||||
|
if expires < time.Now().Unix() && expires != 0 {
|
||||||
|
return ErrSignExpired
|
||||||
|
}
|
||||||
|
// verify sign
|
||||||
|
if s.Sign(data, expires) != sign {
|
||||||
|
return ErrSignInvalid
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHMACSign(secret []byte) Sign {
|
||||||
|
return HMACSign{SecretKey: secret}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package sign
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
type Sign interface {
|
||||||
|
Sign(data string, expire int64) string
|
||||||
|
Verify(data, sign string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrSignExpired = errors.New("sign expired")
|
||||||
|
ErrSignInvalid = errors.New("sign invalid")
|
||||||
|
ErrExpireInvalid = errors.New("expire invalid")
|
||||||
|
ErrExpireMissing = errors.New("expire missing")
|
||||||
|
)
|
|
@ -33,17 +33,18 @@ func FsGet(c *gin.Context) {
|
||||||
common.ErrorStrResp(c, "password is incorrect", 401)
|
common.ErrorStrResp(c, "password is incorrect", 401)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data, err := fs.Get(c, req.Path)
|
obj, err := fs.Get(c, req.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 500, true)
|
common.ErrorResp(c, err, 500, true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
common.SuccessResp(c, FsGetResp{
|
common.SuccessResp(c, FsGetResp{
|
||||||
ObjResp: ObjResp{
|
ObjResp: ObjResp{
|
||||||
Name: data.GetName(),
|
Name: obj.GetName(),
|
||||||
Size: data.GetSize(),
|
Size: obj.GetSize(),
|
||||||
IsDir: data.IsDir(),
|
IsDir: obj.IsDir(),
|
||||||
Modified: data.ModTime(),
|
Modified: obj.ModTime(),
|
||||||
|
Sign: Sign(obj),
|
||||||
},
|
},
|
||||||
// TODO: set raw url
|
// TODO: set raw url
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/alist-org/alist/v3/internal/db"
|
"github.com/alist-org/alist/v3/internal/db"
|
||||||
"github.com/alist-org/alist/v3/internal/fs"
|
"github.com/alist-org/alist/v3/internal/fs"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/internal/setting"
|
"github.com/alist-org/alist/v3/internal/setting"
|
||||||
|
"github.com/alist-org/alist/v3/internal/sign"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/alist-org/alist/v3/server/common"
|
"github.com/alist-org/alist/v3/server/common"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
@ -24,7 +24,7 @@ type ObjResp struct {
|
||||||
Size int64 `json:"size"`
|
Size int64 `json:"size"`
|
||||||
IsDir bool `json:"is_dir"`
|
IsDir bool `json:"is_dir"`
|
||||||
Modified time.Time `json:"modified"`
|
Modified time.Time `json:"modified"`
|
||||||
URL string `json:"url"`
|
Sign string `json:"sign"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FsListResp struct {
|
type FsListResp struct {
|
||||||
|
@ -99,9 +99,20 @@ func toObjResp(objs []model.Obj, path string, baseURL string) []ObjResp {
|
||||||
Size: obj.GetSize(),
|
Size: obj.GetSize(),
|
||||||
IsDir: obj.IsDir(),
|
IsDir: obj.IsDir(),
|
||||||
Modified: obj.ModTime(),
|
Modified: obj.ModTime(),
|
||||||
// TODO: sign url
|
Sign: Sign(obj),
|
||||||
URL: fmt.Sprintf("%s/d%s", baseURL, stdpath.Join(path, obj.GetName())),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Sign(obj model.Obj) string {
|
||||||
|
if obj.IsDir() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
expire := setting.GetIntSetting("link_expiration", 0)
|
||||||
|
if expire == 0 {
|
||||||
|
return sign.NotExpired(obj.GetName())
|
||||||
|
} else {
|
||||||
|
return sign.WithDuration(obj.GetName(), time.Duration(expire)*time.Hour)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"github.com/alist-org/alist/v3/internal/conf"
|
"github.com/alist-org/alist/v3/internal/conf"
|
||||||
"github.com/alist-org/alist/v3/internal/db"
|
"github.com/alist-org/alist/v3/internal/db"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/internal/sign"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils/random"
|
"github.com/alist-org/alist/v3/pkg/utils/random"
|
||||||
"github.com/alist-org/alist/v3/server/common"
|
"github.com/alist-org/alist/v3/server/common"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
@ -17,6 +18,7 @@ func ResetToken(c *gin.Context) {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
sign.Instance()
|
||||||
common.SuccessResp(c, token)
|
common.SuccessResp(c, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ func Init(r *gin.Engine) {
|
||||||
public := api.Group("/public")
|
public := api.Group("/public")
|
||||||
public.GET("/settings", controllers.PublicSettings)
|
public.GET("/settings", controllers.PublicSettings)
|
||||||
public.Any("/list", controllers.FsList)
|
public.Any("/list", controllers.FsList)
|
||||||
public.GET("/get", controllers.FsGet)
|
public.Any("/get", controllers.FsGet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Cors(r *gin.Engine) {
|
func Cors(r *gin.Engine) {
|
||||||
|
|
Loading…
Reference in New Issue