From 3948d517281dded3d09ab11c0bf956dc225e9419 Mon Sep 17 00:00:00 2001 From: veypi Date: Wed, 25 Sep 2024 16:19:10 +0800 Subject: [PATCH] feat: token gen --- oa/api/app/resource.go | 4 +- oa/api/token/token.go | 126 +++++++++++++++++++++++++++++++--------- oa/api/user/user.go | 30 ++++++++-- oa/cfg/cfg.go | 10 +++- oa/cfg/db.go | 6 +- oa/errs/errors.go | 29 ++++++--- oa/libs/auth/access.go | 3 +- oa/libs/auth/jwt.go | 33 +++++++---- oa/models/access.gen.go | 2 +- oa/models/access.go | 11 ++-- oa/models/app.go | 67 ++++++++++++++++----- oa/models/init.go | 54 ++++++++++++++++- oa/models/role.go | 11 ++-- oa/models/token.gen.go | 24 +++++--- oa/models/user.gen.go | 2 + oa/models/user.go | 26 +++++++-- 16 files changed, 340 insertions(+), 98 deletions(-) diff --git a/oa/api/app/resource.go b/oa/api/app/resource.go index 5d71d01..a525442 100644 --- a/oa/api/app/resource.go +++ b/oa/api/app/resource.go @@ -34,8 +34,8 @@ func resourceDelete(x *rest.X) (any, error) { return nil, err } data := &M.Resource{ - AppID: opts.AppID, - Name: opts.Name, + AppID: opts.AppID, + Name: opts.Name, } err = cfg.DB().Delete(data).Error diff --git a/oa/api/token/token.go b/oa/api/token/token.go index 37b6e7e..69b77e5 100644 --- a/oa/api/token/token.go +++ b/oa/api/token/token.go @@ -1,16 +1,21 @@ package token import ( + "encoding/hex" "oa/cfg" + "oa/errs" + "oa/libs/auth" M "oa/models" - "strings" + "time" - "github.com/google/uuid" + "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.Get("/salt/:id", tokenSalt) + r.Post("/salt", tokenSalt) r.Post("/", tokenPost) r.Get("/:token_id", tokenGet) r.Patch("/:token_id", tokenPatch) @@ -24,10 +29,100 @@ func tokenSalt(x *rest.X) (any, error) { 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("id = ?", opts.ID).First(data).Error - return data.Salt, err + 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.Token != nil { + // for other app redirect + oldClaim, err := auth.ParseJwt(*opts.Token) + if err != nil { + return nil, err + } + err = cfg.DB().Where("id = ?", oldClaim.ID).First(data).Error + if err != nil { + return nil, err + } + if oldClaim.AID == *opts.AppID { + // refresh token + } else { + // gen other app token + } + } else if opts.Salt != nil && opts.Code != nil && aid == cfg.Config.ID { + // 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)) + logv.Warn().Msgf("%d: %d", len(key), len(salt)) + logv.Warn().Msgf("%s: %s", user.Code, *opts.Salt) + de, err := utils.AesDecrypt([]byte(code), key, salt) + if err != nil || de != user.ID { + return nil, errs.AuthInvalid + } + 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 + } + // logv.AssertError(cfg.DB().Create(data).Error) + claim.AID = aid + claim.UID = user.ID + claim.Name = user.Username + claim.Icon = user.Icon + if user.Nickname != "" { + claim.Name = user.Nickname + } + acList := make(auth.Access, 0, 10) + logv.AssertError(cfg.DB().Debug().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 = ?", user.ID). + Scan(&acList).Error) + claim.Access = acList + token := logv.AssertFuncErr(auth.GenJwt(claim)) + return map[string]string{"refresh": token, "token": token}, err + } else { + return nil, errs.ArgsInvalid + } + claim.ExpiresAt = jwt.NewNumericDate(data.ExpiredAt) + err = cfg.DB().Create(data).Error + + return data, err +} + func tokenGet(x *rest.X) (any, error) { opts := &M.TokenGet{} err := x.Parse(opts) @@ -75,27 +170,6 @@ func tokenDelete(x *rest.X) (any, error) { return data, err } -func tokenPost(x *rest.X) (any, error) { - opts := &M.TokenPost{} - err := x.Parse(opts) - if err != nil { - return nil, err - } - data := &M.Token{} - - data.ID = strings.ReplaceAll(uuid.New().String(), "-", "") - data.UserID = opts.UserID - data.AppID = opts.AppID - if opts.ExpiredAt != nil { - data.ExpiredAt = *opts.ExpiredAt - } - if opts.OverPerm != nil { - data.OverPerm = *opts.OverPerm - } - err = cfg.DB().Create(data).Error - - return data, err -} func tokenList(x *rest.X) (any, error) { opts := &M.TokenList{} err := x.Parse(opts) diff --git a/oa/api/user/user.go b/oa/api/user/user.go index 0ffbd14..603718e 100644 --- a/oa/api/user/user.go +++ b/oa/api/user/user.go @@ -12,6 +12,7 @@ import ( "github.com/google/uuid" "github.com/veypi/OneBD/rest" + "gorm.io/gorm" ) func useUser(r rest.Router) { @@ -121,8 +122,8 @@ func userPost(x *rest.X) (any, error) { data.Username = opts.Username data.Salt = opts.Salt data.Code = opts.Code - if data.Username == "" || len(data.Salt) != 32 || len(data.Code) != 256 { - return nil, errs.ArgsInvalid + if data.Username == "" || len(data.Salt) != 32 || len(data.Code) != 64 { + return nil, errs.ArgsInvalid.WithStr("username/salt/code length") } if opts.Nickname != nil { data.Nickname = *opts.Nickname @@ -139,9 +140,26 @@ func userPost(x *rest.X) (any, error) { data.Phone = *opts.Phone } data.Status = 1 - err = cfg.DB().Create(data).Error - if err != nil { - return nil, err - } + err = cfg.DB().Transaction(func(tx *gorm.DB) error { + err := tx.Create(data).Error + if err != nil { + return err + } + app := &M.App{} + err = tx.Where("id = ?", cfg.Config.ID).First(app).Error + if err != nil { + return err + } + status := "ok" + if app.Participate != "auto" { + status = "applying" + } + + return tx.Create(&M.AppUser{ + UserID: data.ID, + AppID: cfg.Config.ID, + Status: status, + }).Error + }) return data, nil } diff --git a/oa/cfg/cfg.go b/oa/cfg/cfg.go index 46b37f7..1a487f7 100644 --- a/oa/cfg/cfg.go +++ b/oa/cfg/cfg.go @@ -9,6 +9,7 @@ package cfg import ( "github.com/veypi/OneBD/rest" + "github.com/veypi/utils" "github.com/veypi/utils/flags" "github.com/veypi/utils/logv" ) @@ -16,7 +17,8 @@ import ( type config struct { rest.RestConf DSN string `json:"dsn"` - JWT string `json:"jwt"` + ID string `json:"id"` + Key string `json:"key"` } var Config = &config{} @@ -34,6 +36,12 @@ func init() { CMD.Before = func() error { flags.LoadCfg(*configFile, Config) CMD.Parse() + if Config.Key == "" { + Config.Key = utils.RandSeq(32) + } + if Config.ID == "" { + Config.ID = utils.RandSeq(32) + } logv.SetLevel(logv.AssertFuncErr(logv.ParseLevel(Config.LoggerLevel))) return nil } diff --git a/oa/cfg/db.go b/oa/cfg/db.go index b13426a..5991f2c 100644 --- a/oa/cfg/db.go +++ b/oa/cfg/db.go @@ -15,8 +15,8 @@ import ( var db *gorm.DB -var cmdDB = CMD.SubCommand("db", "database operations") -var cmdMigrate = cmdDB.SubCommand("migrate", "migrate database") +var CmdDB = CMD.SubCommand("db", "database operations") +var cmdMigrate = CmdDB.SubCommand("migrate", "migrate database") var ObjList = make([]any, 0, 10) func init() { @@ -31,7 +31,7 @@ func init() { DB().DisableForeignKeyConstraintWhenMigrating = false return DB().AutoMigrate(ObjList...) } - cmdDB.SubCommand("drop", "drop database").Command = func() error { + CmdDB.SubCommand("drop", "drop database").Command = func() error { return DB().Migrator().DropTable(ObjList...) } } diff --git a/oa/errs/errors.go b/oa/errs/errors.go index 39ffba6..dc1108f 100644 --- a/oa/errs/errors.go +++ b/oa/errs/errors.go @@ -24,8 +24,13 @@ func JsonResponse(x *rest.X, data any) error { } func JsonErrorResponse(x *rest.X, err error) { - code := 50000 - var msg string + code, msg := errIter(err) + x.WriteHeader(code / 100) + x.JSON(map[string]any{"code": code, "err": msg}) +} + +func errIter(err error) (code int, msg string) { + code = 50000 switch e := err.(type) { case *CodeErr: code = e.Code @@ -38,6 +43,8 @@ func JsonErrorResponse(x *rest.X, err error) { logv.Warn().Msgf("unhandled db error %d: %s", e.Number, err) msg = "db error" } + case interface{ Unwrap() error }: + return errIter(e.Unwrap()) default: if errors.Is(e, gorm.ErrRecordNotFound) { code = NotFound.Code @@ -50,8 +57,7 @@ func JsonErrorResponse(x *rest.X, err error) { msg = e.Error() } } - x.WriteHeader(code / 100) - x.JSON(map[string]any{"code": code, "err": msg}) + return } type CodeErr struct { @@ -64,13 +70,19 @@ func (c *CodeErr) Error() string { } func (c *CodeErr) WithErr(e error) error { - c.Msg = fmt.Errorf("%s: %w", c.Msg, e).Error() - return c + nerr := &CodeErr{ + Code: c.Code, + Msg: fmt.Errorf("%s: %w", c.Msg, e).Error(), + } + return nerr } func (c *CodeErr) WithStr(m string) error { - c.Msg = fmt.Errorf("%s: %s", c.Msg, m).Error() - return c + nerr := &CodeErr{ + Code: c.Code, + Msg: fmt.Errorf("%s: %s", c.Msg, m).Error(), + } + return nerr } // New creates a new CodeMsg. @@ -87,4 +99,5 @@ var ( AuthInvalid = New(40103, "auth invalid") AuthNoPerm = New(40104, "no permission") NotFound = New(40400, "not found") + DBError = New(50010, "db error") ) diff --git a/oa/libs/auth/access.go b/oa/libs/auth/access.go index 362a6e0..dd15c80 100644 --- a/oa/libs/auth/access.go +++ b/oa/libs/auth/access.go @@ -21,7 +21,7 @@ const ( DoAll = 5 ) -type Access []struct { +type Access []*struct { Name string `json:"name"` TID string `json:"tid"` Level AuthLevel `json:"level"` @@ -43,6 +43,7 @@ func (a *Access) Check(target string, tid string, l AuthLevel) bool { type Claims struct { UID string `json:"uid"` + AID string `json:"aid"` Name string `json:"name"` Icon string `json:"icon"` Access Access `json:"access"` diff --git a/oa/libs/auth/jwt.go b/oa/libs/auth/jwt.go index 73630bb..01e83a7 100644 --- a/oa/libs/auth/jwt.go +++ b/oa/libs/auth/jwt.go @@ -21,16 +21,31 @@ import ( func GenJwt(claim *Claims) (string, error) { if claim.ExpiresAt == nil { - claim.ExpiresAt = jwt.NewNumericDate(time.Now().Add(5 * time.Minute)) + claim.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Hour)) } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim) - tokenString, err := token.SignedString(cfg.Config.JWT) + tokenString, err := token.SignedString([]byte(cfg.Config.Key)) if err != nil { return "", err } return tokenString, nil } +func ParseJwt(tokenString string) (*Claims, error) { + claims := &Claims{} + token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + return []byte(cfg.Config.Key), nil + }) + + if err != nil || !token.Valid { + return nil, errs.AuthInvalid + } + return claims, nil +} + func CheckJWT(x *rest.X) (*Claims, error) { authHeader := x.Request.Header.Get("Authorization") if authHeader == "" { @@ -43,17 +58,11 @@ func CheckJWT(x *rest.X) (*Claims, error) { } // Parse the token - claims := &Claims{} - token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) - } - return []byte(cfg.Config.JWT), nil - }) - - if err != nil || !token.Valid { - return nil, errs.AuthInvalid + claims, err := ParseJwt(tokenString) + if err != nil { + return nil, err } + x.Request = x.Request.WithContext(context.WithValue(x.Request.Context(), "uid", claims.ID)) return claims, nil } diff --git a/oa/models/access.gen.go b/oa/models/access.gen.go index 7542911..04da31a 100644 --- a/oa/models/access.gen.go +++ b/oa/models/access.gen.go @@ -17,5 +17,5 @@ type AccessPost struct { RoleID *string `json:"role_id" gorm:"index;type:varchar(32);default: null" parse:"json"` Name string `json:"name" parse:"json"` TID string `json:"tid" parse:"json"` - Level string `json:"level" parse:"json"` + Level uint `json:"level" parse:"json"` } diff --git a/oa/models/access.go b/oa/models/access.go index 722056a..51da649 100644 --- a/oa/models/access.go +++ b/oa/models/access.go @@ -10,15 +10,16 @@ package models type Access struct { BaseDate AppID string `json:"app_id" gorm:"index;type:varchar(32)" methods:"post,list" parse:"json"` - App *App `json:"app" gorm:"foreignKey:ID;references:AppID"` + App *App `json:"-" gorm:"foreignKey:AppID;references:ID"` UserID *string `json:"user_id" gorm:"index;type:varchar(32);default: null" methods:"post,list" parse:"json"` - User *User `json:"user"` + User *User `json:"-" gorm:"foreignKey:UserID;references:ID"` RoleID *string `json:"role_id" gorm:"index;type:varchar(32);default: null" methods:"post,list" parse:"json"` - Role *Role `json:"role"` + Role *Role `json:"-" gorm:"foreignKey:RoleID;references:ID"` + + Name string `json:"name" methods:"post,*list" parse:"json"` - Name string `json:"name" methods:"post,*list" parse:"json"` TID string `json:"tid" methods:"post" parse:"json"` - Level string `json:"level" methods:"post" parse:"json"` + Level uint `json:"level" methods:"post" parse:"json"` } diff --git a/oa/models/app.go b/oa/models/app.go index f2bac9e..65c0ba2 100644 --- a/oa/models/app.go +++ b/oa/models/app.go @@ -1,31 +1,70 @@ package models +import ( + "github.com/veypi/utils/logv" + "gorm.io/gorm" +) + type App struct { BaseModel - Name string `json:"name" methods:"get,post,*patch,*list" parse:"json"` - Icon string `json:"icon" methods:"post,*patch" parse:"json"` - Des string `json:"des" methods:"post,*patch" parse:"json"` - Participate string `json:"participate" gorm:"default:auto" methods:"post,*patch" parse:"json"` - InitRoleID string `json:"init_role_id" gorm:"index;type:varchar(32)" methods:"*patch" parse:"json"` - InitRole *Role `json:"init_role" gorm:"foreignKey:ID;references:InitRoleID"` - InitUrl string `json:"init_url"` - UserCount uint `json:"user_count"` - Key string `json:"-"` + Name string `json:"name" methods:"get,post,*patch,*list" parse:"json"` + Icon string `json:"icon" methods:"post,*patch" parse:"json"` + Des string `json:"des" methods:"post,*patch" parse:"json"` + Participate string `json:"participate" gorm:"default:auto" methods:"post,*patch" parse:"json"` + InitRoleID *string `json:"init_role_id" gorm:"index;type:varchar(32);default: null" methods:"*patch" parse:"json"` + InitRole *Role `json:"init_role" gorm:"foreignKey:InitRoleID;references:ID"` + InitUrl string `json:"init_url"` + UserCount uint `json:"user_count"` + Key string `json:"-"` } type AppUser struct { BaseModel - AppID string `json:"app_id" methods:"get,*list,post,*patch" parse:"path"` - App *App `json:"app"` - UserID string `json:"user_id" methods:"get,*list,post,*patch" parse:"path"` - User *User `json:"user"` + AppID string `json:"app_id" methods:"get,*list,post" parse:"path"` + App *App `json:"-" gorm:"foreignKey:AppID;references:ID"` + + UserID string `json:"user_id" methods:"get,*list,post" parse:"path"` + User *User `json:"-" gorm:"foreignKey:UserID;references:ID"` + Status string `json:"status" methods:"post,*patch,*list" parse:"json"` } +func (m *AppUser) onOk(tx *gorm.DB) (err error) { + app := &App{} + logv.AssertError(tx.Where("id = ?", m.AppID).First(app).Error) + if app.InitRoleID != nil { + urList := make([]*UserRole, 0, 2) + logv.AssertError(tx.Where("app_id = ? && user_id = ?", m.AppID, m.UserID).Find(&urList).Error) + if len(urList) == 0 { + return tx.Create(&UserRole{ + AppID: m.AppID, + UserID: m.UserID, + RoleID: *app.InitRoleID, + Status: "ok", + }).Error + } + } + return nil +} + +func (m *AppUser) AfterCreate(tx *gorm.DB) error { + if m.Status == "ok" { + logv.AssertError(m.onOk(tx)) + } + return tx.Model(&App{}).Where("id = ?", m.AppID).Update("user_count", gorm.Expr("user_count + ?", 1)).Error +} + +func (m *AppUser) AfterUpdate(tx *gorm.DB) error { + if m.Status == "ok" { + return m.onOk(tx) + } + return nil +} + type Resource struct { BaseDate AppID string `json:"app_id" gorm:"primaryKey;type:varchar(32)" methods:"post,list,delete" parse:"json"` - App *App `json:"app" gorm:"foreignKey:ID;references:AppID"` + App *App `json:"-" gorm:"foreignKey:AppID;references:ID"` Name string `json:"name" gorm:"primaryKey" methods:"post,delete" parse:"json"` Des string `json:"des" methods:"post" parse:"json"` } diff --git a/oa/models/init.go b/oa/models/init.go index 8a0bb95..e7fefa9 100644 --- a/oa/models/init.go +++ b/oa/models/init.go @@ -7,9 +7,13 @@ package models import ( - "gorm.io/gorm" "oa/cfg" + "strings" "time" + + "github.com/google/uuid" + "github.com/veypi/utils/logv" + "gorm.io/gorm" ) type BaseModel struct { @@ -18,6 +22,13 @@ type BaseModel struct { BaseDate } +func (m *BaseModel) BeforeCreate(tx *gorm.DB) error { + if m.ID == "" { + m.ID = strings.ReplaceAll(uuid.New().String(), "-", "") + } + return nil +} + type BaseDate struct { CreatedAt time.Time `json:"created_at" methods:"*list" parse:"query"` UpdatedAt time.Time `json:"updated_at" methods:"*list" parse:"query"` @@ -25,6 +36,7 @@ type BaseDate struct { } func init() { + cfg.CmdDB.SubCommand("init", "init db data").Command = InitDBData cfg.ObjList = append(cfg.ObjList, &AppUser{}) cfg.ObjList = append(cfg.ObjList, &Resource{}) cfg.ObjList = append(cfg.ObjList, &Access{}) @@ -34,3 +46,43 @@ func init() { cfg.ObjList = append(cfg.ObjList, &Token{}) cfg.ObjList = append(cfg.ObjList, &App{}) } + +func InitDBData() error { + app := &App{} + app.ID = cfg.Config.ID + logv.AssertError(cfg.DB().Where("id = ?", app.ID).Attrs(app).FirstOrCreate(app).Error) + initRole := map[string]map[string]uint{ + "user": {"admin": 5, "normal": 1}, + "app": {"admin": 5, "normal": 2}, + } + adminID := "" + for r, roles := range initRole { + logv.AssertError(cfg.DB().Where("app_id = ? AND name = ?", app.ID, r).FirstOrCreate(&Resource{ + AppID: app.ID, + Name: r, + }).Error) + for rName, l := range roles { + role := &Role{} + logv.AssertError(cfg.DB().Where("app_id = ? AND name = ?", app.ID, rName).Attrs(&Role{ + BaseModel: BaseModel{ + ID: strings.ReplaceAll(uuid.New().String(), "-", ""), + }, + AppID: app.ID, + Name: rName, + }).FirstOrCreate(role).Error) + logv.AssertError(cfg.DB().Where("app_id = ? AND role_id = ? AND name = ?", app.ID, role.ID, r).FirstOrCreate(&Access{ + AppID: app.ID, + RoleID: &role.ID, + Name: r, + Level: l, + }).Error) + if rName == "admin" { + adminID = role.ID + } + } + } + if app.InitRoleID == nil { + logv.AssertError(cfg.DB().Model(app).Update("init_role_id", adminID).Error) + } + return nil +} diff --git a/oa/models/role.go b/oa/models/role.go index f2b26cc..91786a0 100644 --- a/oa/models/role.go +++ b/oa/models/role.go @@ -2,9 +2,10 @@ package models type Role struct { BaseModel - Name string `json:"name" methods:"post,*patch,*list" parse:"json"` - Des string `json:"des" methods:"post,*patch" parse:"json"` - AppID string `json:"app_id" gorm:"index;type:varchar(32)" methods:"post,*patch" parse:"json"` - App *App `json:"app" gorm:"foreignKey:ID;references:AppID"` - UserCount uint `json:"user_count"` + Name string `json:"name" methods:"post,*patch,*list" parse:"json"` + Des string `json:"des" methods:"post,*patch" parse:"json"` + AppID string `json:"app_id" gorm:"index;type:varchar(32)" methods:"post,*patch" parse:"json"` + App *App `json:"-" gorm:"foreignKey:AppID;references:ID"` + UserCount uint `json:"user_count"` + Access []*Access `json:"-"` } diff --git a/oa/models/token.gen.go b/oa/models/token.gen.go index 65cfd65..8de012e 100644 --- a/oa/models/token.gen.go +++ b/oa/models/token.gen.go @@ -3,8 +3,23 @@ package models import "time" type TokenSalt struct { - ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path"` + Username string `json:"username" parse:"json"` + Typ *string `json:"typ" parse:"json"` } + +type TokenPost struct { + UserID string `json:"user_id" gorm:"index;type:varchar(32)" parse:"json"` + + // 两种获取token方式,一种用token换取(应用登录),一种用密码加密code换(oa登录) + Token *string `json:"token" parse:"json"` + Salt *string `json:"salt" parse:"json"` + Code *string `json:"code" parse:"json"` + + AppID *string `json:"app_id" gorm:"index;type:varchar(32)" parse:"json"` + ExpiredAt *time.Time `json:"expired_at" parse:"json"` + OverPerm *string `json:"over_perm" parse:"json"` +} + type TokenGet struct { ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@token_id"` } @@ -19,13 +34,6 @@ type TokenDelete struct { ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@token_id"` } -type TokenPost struct { - UserID string `json:"user_id" gorm:"index;type:varchar(32)" parse:"json"` - AppID string `json:"app_id" gorm:"index;type:varchar(32)" parse:"json"` - ExpiredAt *time.Time `json:"expired_at" parse:"json"` - OverPerm *string `json:"over_perm" parse:"json"` -} - type TokenList struct { UserID string `json:"user_id" gorm:"index;type:varchar(32)" parse:"json"` AppID string `json:"app_id" gorm:"index;type:varchar(32)" parse:"json"` diff --git a/oa/models/user.gen.go b/oa/models/user.gen.go index 1be4e38..76a7e8d 100644 --- a/oa/models/user.gen.go +++ b/oa/models/user.gen.go @@ -57,12 +57,14 @@ type UserRoleDelete struct { ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@user_role_id"` UserID string `json:"user_id" parse:"path"` RoleID string `json:"role_id" parse:"path"` + AppID string `json:"app_id" parse:"json"` } type UserRolePost struct { UserID string `json:"user_id" parse:"path"` RoleID string `json:"role_id" parse:"path"` Status string `json:"status" parse:"json"` + AppID string `json:"app_id" parse:"json"` } type UserRoleList struct { diff --git a/oa/models/user.go b/oa/models/user.go index 5f9d90e..4ba6688 100644 --- a/oa/models/user.go +++ b/oa/models/user.go @@ -1,5 +1,12 @@ package models +import ( + "gorm.io/gorm" +) + +// salt for user user password gen aes code +// salt 32 hex / 16 byte / 128 bit +// code 64 hex / 32 byte / 256 bit type User struct { BaseModel Username string `json:"username" gorm:"type:varchar(100);unique;default:not null" methods:"post,*patch,*list" parse:"json"` @@ -12,14 +19,23 @@ type User struct { Status uint `json:"status" methods:"*patch,*list" parse:"json"` Salt string `json:"-" gorm:"type:varchar(32)" methods:"post" parse:"json"` - Code string `json:"-" gorm:"type:varchar(256)" methods:"post" parse:"json"` + Code string `json:"-" gorm:"type:varchar(64)" methods:"post" parse:"json"` } type UserRole struct { BaseModel - UserID string `json:"user_id" methods:"post,delete" parse:"path"` - User *User `json:"user"` - RoleID string `json:"role_id" methods:"post,delete" parse:"path"` - Role *Role `json:"role"` + UserID string `json:"user_id" methods:"post,delete" parse:"json"` + User *User `json:"-" gorm:"foreignKey:UserID;references:ID"` + + RoleID string `json:"role_id" methods:"post,delete" parse:"json"` + Role *Role `json:"-" gorm:"foreignKey:RoleID;references:ID"` + + AppID string `json:"app_id" methods:"post,delete" parse:"json"` + App *App `json:"-" gorm:"foreignKey:AppID;references:ID"` + Status string `json:"status" methods:"post,*patch,*list" parse:"json"` } + +func (m *UserRole) AfterCreate(tx *gorm.DB) error { + return tx.Model(&Role{}).Where("id = ?", m.RoleID).Update("user_count", gorm.Expr("user_count + ?", 1)).Error +}