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/api/user/user.go

271 lines
7.0 KiB
Go

package user
import (
"encoding/base64"
"errors"
"fmt"
"github.com/veypi/OneAuth/cfg"
"github.com/veypi/OneAuth/libs/app"
"github.com/veypi/OneAuth/libs/auth"
"github.com/veypi/OneAuth/libs/base"
"github.com/veypi/OneAuth/libs/oerr"
"github.com/veypi/OneAuth/models"
"github.com/veypi/OneBD"
"github.com/veypi/OneBD/rfc"
"github.com/veypi/utils/log"
"gorm.io/gorm"
"math/rand"
"strconv"
"time"
)
func Router(r OneBD.Router) {
pool := OneBD.NewHandlerPool(func() OneBD.Handler {
h := &handler{}
h.Ignore(rfc.MethodHead, rfc.MethodPost)
return h
})
r.Set("/", pool, rfc.MethodGet, rfc.MethodPost) // list
r.Set("/:user_id", pool, rfc.MethodGet, rfc.MethodPatch, rfc.MethodHead, rfc.MethodDelete)
r.Set("/:user_id/role/", userRoleP, rfc.MethodPost)
r.Set("/:user_id/role/:role_id", userRoleP, rfc.MethodDelete)
//r.WS("/ws", func(m OneBD.Meta) (conn OneBD.WebsocketConn, err error) {
//return ws.User.Upgrade(m.ResponseWriter(), m.Request())
//})
}
type handler struct {
base.ApiHandler
User *models.User
}
// Get get user data
func (h *handler) Get() (interface{}, error) {
userID := uint(h.Meta().ParamsInt("user_id"))
if userID != h.Payload.ID && !h.Payload.GetAuth(auth.User, "").CanRead() {
return nil, oerr.NoAuth.AttachStr("to read user data")
}
if userID != 0 {
user := &models.User{}
user.ID = userID
return user, cfg.DB().Preload("Apps.App").Where(user).First(user).Error
} else {
username := h.Meta().Query("username")
if username != "" {
users := make([]*models.User, 0, 10)
err := cfg.DB().Where("username LIKE ? OR nickname LIKE ?", "%"+username+"%", "%"+username+"%").Find(&users).Error
if err != nil {
return nil, err
}
return users, nil
}
users := make([]models.User, 10)
skip, err := strconv.Atoi(h.Meta().Query("skip"))
if err != nil || skip < 0 {
skip = 0
}
if err := cfg.DB().Offset(skip).Find(&users).Error; err != nil {
return nil, err
}
return users, nil
}
}
// Post register user
func (h *handler) Post() (interface{}, error) {
self := &models.App{}
self.UUID = cfg.CFG.APPUUID
err := cfg.DB().Where(self).First(self).Error
if err != nil {
return nil, oerr.DBErr.Attach(err)
}
log.Warn().Msgf("%v %v", self.EnableRegister, h.GetAuth(auth.User, "").CanCreate())
if !self.EnableRegister && !h.Payload.GetAuth(auth.User, "").CanCreate() {
return nil, errors.New("register disabled")
}
var userdata = struct {
Username string
Password string
Nickname string
Phone string
Email string
Domain string
Title string
Position string
}{}
if err := h.Meta().ReadJson(&userdata); err != nil {
return nil, err
}
pass, err := base64.StdEncoding.DecodeString(userdata.Password)
if err != nil {
return nil, err
}
if len(pass) > 32 || len(pass) < 6 {
return nil, oerr.PassError
}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
h.User = new(models.User)
h.User.Icon = fmt.Sprintf("/public/icon/default/%04d.jpg", r.Intn(230))
h.User.Nickname = userdata.Nickname
h.User.Phone = userdata.Phone
h.User.Username = userdata.Username
h.User.Email = userdata.Email
h.User.Position = userdata.Position
if err := h.User.UpdatePass(string(pass)); err != nil {
log.HandlerErrs(err)
return nil, oerr.ResourceCreatedFailed
}
err = cfg.DB().Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&h.User).Error; err != nil {
return oerr.ResourceDuplicated
}
_, err := app.AddUser(tx, self.UUID, h.User.ID, self.InitRoleID, models.AUOK)
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
return h.User, nil
}
// Patch update user data
func (h *handler) Patch() (interface{}, error) {
uid := h.Meta().Params("user_id")
opts := struct {
Password string
Icon string
Nickname string
Phone string `gorm:"type:varchar(20);unique;default:null" json:",omitempty"`
Email string `gorm:"type:varchar(50);unique;default:null" json:",omitempty"`
Status string
Position string
}{}
if err := h.Meta().ReadJson(&opts); err != nil {
return nil, err
}
target := models.User{}
if tempID, err := strconv.Atoi(uid); err != nil || tempID <= 0 {
return nil, oerr.ApiArgsError.Attach(err)
} else {
target.ID = uint(tempID)
}
if err := cfg.DB().Where(&target).First(&target).Error; err != nil {
return nil, err
}
if target.ID != h.Payload.ID && h.Payload.GetAuth(auth.User, strconv.Itoa(int(target.ID))).CanUpdate() {
return nil, oerr.NoAuth
}
if len(opts.Password) >= 6 {
if err := target.UpdatePass(opts.Password); err != nil {
log.HandlerErrs(err)
return nil, oerr.ApiArgsError.AttachStr(err.Error())
}
}
if opts.Nickname != "" {
target.Nickname = opts.Nickname
}
if opts.Icon != "" {
target.Icon = opts.Icon
}
if opts.Position != "" {
target.Position = opts.Position
}
if opts.Phone != "" {
target.Phone = opts.Phone
}
if opts.Email != "" {
target.Email = opts.Email
}
if opts.Status != "" {
target.Status = opts.Status
}
if err := cfg.DB().Updates(&target).Error; err != nil {
return nil, err
}
return nil, nil
}
// Delete delete user
func (h *handler) Delete() (interface{}, error) {
// TODO::
return nil, nil
}
// Head user login
func (h *handler) Head() (interface{}, error) {
uid := h.Meta().Params("user_id")
pass, err := base64.StdEncoding.DecodeString(h.Meta().Query("password"))
if err != nil {
return nil, oerr.ApiArgsError.Attach(err)
}
password := string(pass)
if len(uid) == 0 || len(password) == 0 {
return nil, oerr.ApiArgsError
}
h.User = new(models.User)
uidType := h.Meta().Query("UidType")
switch uidType {
case "username":
h.User.Username = uid
case "phone":
h.User.Phone = uid
case "email":
h.User.Email = uid
default:
h.User.Username = uid
}
target := &models.App{}
target.UUID = cfg.CFG.APPUUID
err = cfg.DB().Where(target).Find(target).Error
if err != nil {
return nil, oerr.DBErr.Attach(err)
}
if err := cfg.DB().Where(h.User).First(h.User).Error; err != nil {
if err.Error() == gorm.ErrRecordNotFound.Error() {
return nil, oerr.AccountNotExist
} else {
log.HandlerErrs(err)
return nil, oerr.DBErr.Attach(err)
}
}
isAuth, err := h.User.CheckLogin(password)
if err != nil || !isAuth {
return nil, oerr.PassError.Attach(err)
}
au := &models.AppUser{}
au.UserID = h.User.ID
au.AppUUID = target.UUID
err = cfg.DB().Where(au).First(au).Error
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, err
}
if !target.EnableRegister {
return nil, errors.New("该应用不允许自主加入,请联系管理员")
}
_, err := app.AddUser(cfg.DB(), target.UUID, h.User.ID, target.InitRoleID, models.AUOK)
if err != nil {
return nil, err
}
} else if au.Status != models.AUOK {
return nil, oerr.DisableLogin
}
err = h.User.LoadAuths(cfg.DB())
if err != nil {
return nil, err
}
tokenStr, err := h.User.GetToken(target.UUID, cfg.CFG.APPKey)
if err != nil {
log.HandlerErrs(err)
return nil, oerr.Unknown.Attach(err)
}
h.Meta().SetHeader("auth_token", tokenStr)
log.Info().Msg(h.User.Username + " login")
return nil, nil
}