|
|
|
package token
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/hex"
|
|
|
|
"net/http"
|
|
|
|
"oa/cfg"
|
|
|
|
"oa/errs"
|
|
|
|
"oa/libs/auth"
|
|
|
|
M "oa/models"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
|
|
"github.com/veypi/OneBD/rest"
|
|
|
|
"github.com/veypi/utils"
|
|
|
|
"github.com/veypi/utils/logv"
|
|
|
|
)
|
|
|
|
|
|
|
|
func useToken(r rest.Router) {
|
|
|
|
r.Post("/salt", tokenSalt)
|
|
|
|
r.Post("/", tokenPost)
|
|
|
|
r.Get("/:token_id", tokenGet)
|
|
|
|
r.Patch("/:token_id", tokenPatch)
|
|
|
|
r.Delete("/:token_id", tokenDelete)
|
|
|
|
r.Get("/", tokenList)
|
|
|
|
}
|
|
|
|
func tokenSalt(x *rest.X) (any, error) {
|
|
|
|
opts := &M.TokenSalt{}
|
|
|
|
err := x.Parse(opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data := &M.User{}
|
|
|
|
query := "username = ?"
|
|
|
|
if opts.Typ == nil {
|
|
|
|
} else if *opts.Typ == "email" {
|
|
|
|
query = "email = ?"
|
|
|
|
} else if *opts.Typ == "phone" {
|
|
|
|
query = "phone = ?"
|
|
|
|
}
|
|
|
|
|
|
|
|
err = cfg.DB().Where(query, opts.Username).First(data).Error
|
|
|
|
return map[string]string{"salt": data.Salt, "id": data.ID}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// for user login app
|
|
|
|
func tokenPost(x *rest.X) (any, error) {
|
|
|
|
opts := &M.TokenPost{}
|
|
|
|
err := x.Parse(opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
aid := cfg.Config.ID
|
|
|
|
if opts.AppID != nil {
|
|
|
|
aid = *opts.AppID
|
|
|
|
}
|
|
|
|
data := &M.Token{}
|
|
|
|
claim := &auth.Claims{}
|
|
|
|
claim.IssuedAt = jwt.NewNumericDate(time.Now())
|
|
|
|
claim.Issuer = "oa"
|
|
|
|
if opts.Refresh != nil {
|
|
|
|
typ := "app"
|
|
|
|
if opts.Typ != nil {
|
|
|
|
typ = *opts.Typ
|
|
|
|
}
|
|
|
|
// for other app redirect
|
|
|
|
refresh, err := auth.ParseJwt(*opts.Refresh)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if refresh.ID == "" {
|
|
|
|
return nil, errs.AuthInvalid
|
|
|
|
}
|
|
|
|
err = cfg.DB().Where("id = ?", refresh.ID).First(data).Error
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if typ == "app" {
|
|
|
|
if refresh.AID == aid {
|
|
|
|
// refresh token
|
|
|
|
claim.AID = refresh.AID
|
|
|
|
claim.UID = refresh.UID
|
|
|
|
claim.Name = refresh.Name
|
|
|
|
claim.Icon = refresh.Icon
|
|
|
|
claim.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Minute * 10))
|
|
|
|
acList := make(auth.Access, 0, 10)
|
|
|
|
logv.AssertError(cfg.DB().Table("accesses a").
|
|
|
|
Select("a.name, a.t_id, a.level").
|
|
|
|
Joins("INNER JOIN user_roles ur ON ur.role_id = a.role_id AND ur.user_id = ?", refresh.UID).
|
|
|
|
Scan(&acList).Error)
|
|
|
|
claim.Access = acList
|
|
|
|
} else {
|
|
|
|
// gen other app token
|
|
|
|
}
|
|
|
|
} else if typ == "ufs" {
|
|
|
|
claim.AID = refresh.AID
|
|
|
|
claim.UID = refresh.UID
|
|
|
|
claim.Name = refresh.Name
|
|
|
|
claim.Icon = refresh.Icon
|
|
|
|
claim.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Minute * 10))
|
|
|
|
claim.Access = auth.Access{
|
|
|
|
{Name: "fs", TID: "/", Level: auth.Do},
|
|
|
|
}
|
|
|
|
token := logv.AssertFuncErr(auth.GenJwt(claim))
|
|
|
|
|
|
|
|
cookie := &http.Cookie{
|
|
|
|
Name: "fstoken", // Cookie 的名称
|
|
|
|
Value: token, // Cookie 的值
|
|
|
|
Path: "/fs/u/", // Cookie 的路径,通常是根路径
|
|
|
|
MaxAge: 600, // Cookie 的最大年龄,单位是秒
|
|
|
|
HttpOnly: true, // 是否仅限 HTTP(S) 访问
|
|
|
|
Secure: false, // 是否通过安全连接传输 Cookie
|
|
|
|
}
|
|
|
|
http.SetCookie(x, cookie)
|
|
|
|
|
|
|
|
return token, nil
|
|
|
|
}
|
|
|
|
} else if opts.Code != nil && aid == cfg.Config.ID && opts.Salt != nil && opts.UserID != nil {
|
|
|
|
// for oa login
|
|
|
|
user := &M.User{}
|
|
|
|
err = cfg.DB().Where("id = ?", opts.UserID).Find(user).Error
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
logv.Info().Str("user", user.ID).Msg("login")
|
|
|
|
code := *opts.Code
|
|
|
|
salt := logv.AssertFuncErr(hex.DecodeString(*opts.Salt))
|
|
|
|
key := logv.AssertFuncErr(hex.DecodeString(user.Code))
|
|
|
|
de, err := utils.AesDecrypt([]byte(code), key, salt)
|
|
|
|
if err != nil || de != user.ID {
|
|
|
|
return nil, errs.AuthFailed
|
|
|
|
}
|
|
|
|
data.UserID = *opts.UserID
|
|
|
|
data.AppID = aid
|
|
|
|
if opts.ExpiredAt != nil {
|
|
|
|
data.ExpiredAt = *opts.ExpiredAt
|
|
|
|
} else {
|
|
|
|
data.ExpiredAt = time.Now().Add(time.Hour * 72)
|
|
|
|
}
|
|
|
|
if opts.OverPerm != nil {
|
|
|
|
data.OverPerm = *opts.OverPerm
|
|
|
|
}
|
|
|
|
if opts.Device != nil {
|
|
|
|
data.Device = *opts.Device
|
|
|
|
}
|
|
|
|
data.Ip = x.GetRemoteIp()
|
|
|
|
logv.AssertError(cfg.DB().Create(data).Error)
|
|
|
|
claim.ID = data.ID
|
|
|
|
claim.AID = aid
|
|
|
|
claim.UID = user.ID
|
|
|
|
claim.Name = user.Username
|
|
|
|
claim.Icon = user.Icon
|
|
|
|
claim.ExpiresAt = jwt.NewNumericDate(data.ExpiredAt)
|
|
|
|
if user.Nickname != "" {
|
|
|
|
claim.Name = user.Nickname
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return nil, errs.ArgsInvalid
|
|
|
|
}
|
|
|
|
|
|
|
|
token := logv.AssertFuncErr(auth.GenJwt(claim))
|
|
|
|
return token, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func tokenGet(x *rest.X) (any, error) {
|
|
|
|
opts := &M.TokenGet{}
|
|
|
|
err := x.Parse(opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data := &M.Token{}
|
|
|
|
|
|
|
|
err = cfg.DB().Where("id = ?", opts.ID).First(data).Error
|
|
|
|
|
|
|
|
return data, err
|
|
|
|
}
|
|
|
|
func tokenPatch(x *rest.X) (any, error) {
|
|
|
|
opts := &M.TokenPatch{}
|
|
|
|
err := x.Parse(opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data := &M.Token{}
|
|
|
|
|
|
|
|
err = cfg.DB().Where("id = ?", opts.ID).First(data).Error
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
optsMap := make(map[string]interface{})
|
|
|
|
if opts.ExpiredAt != nil {
|
|
|
|
optsMap["expired_at"] = opts.ExpiredAt
|
|
|
|
}
|
|
|
|
if opts.OverPerm != nil {
|
|
|
|
optsMap["over_perm"] = opts.OverPerm
|
|
|
|
}
|
|
|
|
err = cfg.DB().Model(data).Updates(optsMap).Error
|
|
|
|
|
|
|
|
return data, err
|
|
|
|
}
|
|
|
|
func tokenDelete(x *rest.X) (any, error) {
|
|
|
|
opts := &M.TokenDelete{}
|
|
|
|
err := x.Parse(opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data := &M.Token{}
|
|
|
|
|
|
|
|
err = cfg.DB().Where("id = ?", opts.ID).Delete(data).Error
|
|
|
|
|
|
|
|
return data, err
|
|
|
|
}
|
|
|
|
func tokenList(x *rest.X) (any, error) {
|
|
|
|
opts := &M.TokenList{}
|
|
|
|
err := x.Parse(opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data := make([]*M.Token, 0, 10)
|
|
|
|
|
|
|
|
query := cfg.DB()
|
|
|
|
query = query.Where("user_id = ?", opts.UserID)
|
|
|
|
query = query.Where("app_id = ?", opts.AppID)
|
|
|
|
err = query.Find(&data).Error
|
|
|
|
|
|
|
|
return data, err
|
|
|
|
}
|