From 2e65154206fc2e154abf9fb95663f2879b2a56e6 Mon Sep 17 00:00:00 2001 From: gazebo Date: Sat, 9 Mar 2019 20:59:18 +0800 Subject: [PATCH] - v2 token format changed - DisableUser - clear all tokens when disable user - handle ding ding user leave event --- business/auth2/auth2.go | 64 +++++++++++++++++++++++++++----- business/jxstore/cms/user2.go | 37 ++++++++++++++++++ business/model/dao/dao_auth2.go | 6 ++- business/model/dao/dao_user2.go | 12 ++++-- controllers/dingding_callback.go | 6 ++- main.go | 13 ++++--- 6 files changed, 117 insertions(+), 21 deletions(-) diff --git a/business/auth2/auth2.go b/business/auth2/auth2.go index 6bdd666cd..5c116454d 100644 --- a/business/auth2/auth2.go +++ b/business/auth2/auth2.go @@ -10,6 +10,7 @@ import ( "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/model" + "git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals/api" "github.com/dchest/captcha" @@ -24,6 +25,7 @@ const ( ) const ( + TokenHeader = "TOKEN" TokenVer = "V2" TokenTypeSep = "." TokenUserEmpty = "NULL" @@ -37,8 +39,11 @@ const ( ) const ( - DefTokenDuration = 7 * 24 * time.Hour // 7天 + DefTokenDuration = 7 * 24 * time.Hour // 正式TOKEN,7天有效期 + TmpTokenDuration = 30 * time.Minute // 临时TOKEN,30分钟有效期 MinCaptchaLen = 4 + MaxCaptchaWidth = 400 + MaxCaptchaHeight = 400 ) type IUser interface { @@ -113,21 +118,23 @@ func RegisterAuther(authType string, handler IAuther) { func createAuthInfo(user IUser, authBindInfo *AuthBindEx) (authInfo *AuthInfo) { token, tokenType := createToken(user) + expireDuration := DefTokenDuration authInfo = &AuthInfo{ AuthBindInfo: authBindInfo, - - LoginTime: time.Now(), - ExpiresIn: time.Now().Add(DefTokenDuration).Unix(), - Token: token, - TokenType: tokenType, + LoginTime: time.Now(), + ExpiresIn: time.Now().Add(DefTokenDuration).Unix(), + Token: token, + TokenType: tokenType, } if user != nil { authInfo.UpdateByIUser(user) globals.SugarLogger.Debugf("createAuthInfo id:%s, id2:%s, mobile:%s, authInfo:%s", authInfo.GetID(), authInfo.GetID2(), authInfo.GetMobile(), utils.Format4Output(authInfo, true)) } else { + expireDuration = TmpTokenDuration + authInfo.ExpiresIn = time.Now().Add(expireDuration).Unix() globals.SugarLogger.Debugf("createAuthInfo authInfo:%s", utils.Format4Output(authInfo, true)) } - SetUserInfo(token, authInfo, DefTokenDuration) + SetUserInfo(token, authInfo, expireDuration) return authInfo } @@ -135,6 +142,12 @@ func CreateCaptcha(width, height, captchaLen int) (captchaInfo *CaptchaInfo, err if captchaLen < MinCaptchaLen { captchaLen = MinCaptchaLen } + if width > MaxCaptchaWidth { + width = MaxCaptchaWidth + } + if height > MaxCaptchaHeight { + height = MaxCaptchaHeight + } captchaInfo = &CaptchaInfo{ ID: captcha.NewLen(captchaLen), } @@ -283,6 +296,7 @@ func Logout(authInfo *AuthInfo) (err error) { } // token缓存相关 +///////////// func RemoveUserInfo(token string) { api.Cacher.Del(token) @@ -300,17 +314,36 @@ func SetUserInfo(token string, authInfo *AuthInfo, duration time.Duration) { api.Cacher.Set(token, authInfo, DefTokenDuration) } +func ClearUserToken(userID string) { + if keys, err := api.Cacher.Keys(strings.Join([]string{ + TokenHeader, + TokenVer, + userID, + "*", + }, TokenTypeSep)); err == nil { + for _, key := range keys { + api.Cacher.Del(key) + } + } +} + +///////////// + func createToken(user IUser) (token string, tokenType int) { userID := TokenUserEmpty + userName := TokenUserEmpty tokenType = TokenTypeOnlyAuth if user != nil { - userID = "[" + user.GetID2() + "]" + userID = user.GetID() + userName = "[" + user.GetID2() + "]" tokenType = TokenTypeNormal } return strings.Join([]string{ + TokenHeader, TokenVer, - time.Now().Format("20060102-150405"), userID, + time.Now().Format("20060102-150405"), + userName, utils.GetUUID(), }, TokenTypeSep), tokenType } @@ -319,7 +352,7 @@ func GetTokenType(token string) (tokenType int) { tokenType = TokenTypeNone if token != "" { tokenPartList := strings.Split(token, TokenTypeSep) - if (len(tokenPartList) == 1) || (len(tokenPartList) == 4 && tokenPartList[2] != TokenUserEmpty) { + if (len(tokenPartList) == 1) || (len(tokenPartList) == 6 && tokenPartList[2] != TokenUserEmpty) { tokenType = TokenTypeNormal } else { tokenType = TokenTypeOnlyAuth @@ -344,3 +377,14 @@ func GuessAuthTypeFromAuthID(authID string) (authType string) { } return AuthTypeNone } + +func DisableUser(userID, operatorUserName string) (err error) { + if _, err = dao.UpdateEntityLogically(dao.GetDB(), &model.AuthBind{}, map[string]interface{}{ + "Status": model.AuthBindStatusDisabled, + }, operatorUserName, map[string]interface{}{ + "UserID": userID, + }); err == nil { + ClearUserToken(userID) + } + return err +} diff --git a/business/jxstore/cms/user2.go b/business/jxstore/cms/user2.go index 9eb37771b..b3b00b402 100644 --- a/business/jxstore/cms/user2.go +++ b/business/jxstore/cms/user2.go @@ -3,12 +3,15 @@ package cms import ( "errors" + "git.rosy.net.cn/baseapi/platformapi/dingdingapi" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/auth2" + "git.rosy.net.cn/jx-callback/business/auth2/authprovider/dingding" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/globals" + "git.rosy.net.cn/jx-callback/globals/api" ) var ( @@ -111,3 +114,37 @@ func CreateUser(user *model.User) (err error) { user.Status = model.UserStatusNormal return dao.CreateEntity(nil, user) } + +func DisableUser(ctx *jxcontext.Context, userID string) (err error) { + userName := ctx.GetUserName() + if _, err = dao.UpdateEntityLogically(dao.GetDB(), &model.User{}, map[string]interface{}{ + "Status": model.UserStatusDisabled, + }, userName, map[string]interface{}{ + "UserID": userID, + }); err == nil { + auth2.DisableUser(userID, userName) + } + return err +} + +func OnDingDingMsg(msg map[string]interface{}) (callbackResponse *dingdingapi.CallbackResponse) { + eventType := utils.Interface2String(msg[dingdingapi.KeyEventType]) + if eventType == dingdingapi.CBTagUserLeaveOrg { + var ( + authBind *model.AuthBind + err error + ) + db := dao.GetDB() + for _, userID := range msg[dingdingapi.KeyUserID].([]interface{}) { + userIDStr := utils.Interface2String(userID) + globals.SugarLogger.Debugf("OnDingDingMsg dingding user:%s left company", userIDStr) + if authBind, err = dao.GetAuthBind(db, "", dingding.AuthTypeStaff, userIDStr); err == nil { // 直接找到了 + globals.SugarLogger.Debugf("OnDingDingMsg dingding user:%s, userID:%s left company", userIDStr, authBind.UserID) + if err = DisableUser(jxcontext.AdminCtx, authBind.UserID); err != nil { + globals.SugarLogger.Errorf("OnDingDingMsg failed with error:%v", err) + } + } + } + } + return api.DingDingAPI.Err2CallbackResponse(nil) +} diff --git a/business/model/dao/dao_auth2.go b/business/model/dao/dao_auth2.go index 29a28866e..1fa5bbac2 100644 --- a/business/model/dao/dao_auth2.go +++ b/business/model/dao/dao_auth2.go @@ -15,10 +15,11 @@ func GetAuthBind(db *DaoDB, userID, authType, authID string) (authBind *model.Au sql := ` SELECT * FROM auth_bind t1 - WHERE t1.deleted_at = ? + WHERE t1.deleted_at = ? AND t1.status = ? ` sqlParams := []interface{}{ utils.DefaultTimeValue, + model.AuthBindStatusNormal, } if userID != "" { sql += " AND t1.user_id = ?" @@ -41,9 +42,10 @@ func GetAuthBindsByAuthID2(db *DaoDB, authID2 string, typeList []string) (authBi sql := ` SELECT * FROM auth_bind t1 - WHERE t1.deleted_at = ? AND t1.auth_id2 = ? AND t1.type IN (` + GenQuestionMarks(len(typeList)) + ")" + WHERE t1.deleted_at = ? AND t1.status = ? AND t1.auth_id2 = ? AND t1.type IN (` + GenQuestionMarks(len(typeList)) + ")" sqlParams := []interface{}{ utils.DefaultTimeValue, + model.AuthBindStatusNormal, authID2, typeList, } diff --git a/business/model/dao/dao_user2.go b/business/model/dao/dao_user2.go index eeb1380a1..5852f4203 100644 --- a/business/model/dao/dao_user2.go +++ b/business/model/dao/dao_user2.go @@ -12,10 +12,11 @@ func GetUserByID(db *DaoDB, fieldName, fieldValue string) (user *model.User, err sql := fmt.Sprintf(` SELECT * FROM user t1 - WHERE t1.deleted_at = ? AND t1.%s = ? + WHERE t1.deleted_at = ? AND t1.status = ? AND t1.%s = ? `, fieldName) sqlParams := []interface{}{ utils.DefaultTimeValue, + model.UserStatusNormal, fieldValue, } globals.SugarLogger.Debugf("GetUserByID sql:%s, sqlParams:%s", sql, utils.Format4Output(sqlParams, false)) @@ -27,8 +28,13 @@ func GetUserBindAuthInfo(db *DaoDB, userID string) (authList []*model.AuthBind, sql := ` SELECT * FROM auth_bind t1 - WHERE t1.deleted_at = ? AND t1.user_id = ? + WHERE t1.deleted_at = ? AND t1.status = ? AND t1.user_id = ? ` - err = GetRows(db, &authList, sql, utils.DefaultTimeValue, userID) + sqlParams := []interface{}{ + utils.DefaultTimeValue, + model.UserStatusNormal, + userID, + } + err = GetRows(db, &authList, sql, sqlParams...) return authList, err } diff --git a/controllers/dingding_callback.go b/controllers/dingding_callback.go index 8eca9a3eb..402c69cd6 100644 --- a/controllers/dingding_callback.go +++ b/controllers/dingding_callback.go @@ -4,6 +4,7 @@ import ( "net/http" "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/jxstore/cms" "git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals/api" "github.com/astaxie/beego" @@ -24,7 +25,10 @@ func (c *DingDingController) Msg() { obj, callbackResponse := api.DingDingAPI.GetCallbackMsg(dataMap, c.Ctx.Input.RequestBody) if callbackResponse == nil { globals.SugarLogger.Debugf("dingding msg, obj:%s", utils.Format4Output(obj, false)) - callbackResponse = api.DingDingAPI.Err2CallbackResponse(nil) + callbackResponse = cms.OnDingDingMsg(dataMap) + if callbackResponse == nil { + callbackResponse = api.DingDingAPI.Err2CallbackResponse(nil) + } } c.Data["json"] = callbackResponse c.ServeJSON() diff --git a/main.go b/main.go index 7dde90678..b7da9cd3a 100644 --- a/main.go +++ b/main.go @@ -102,11 +102,14 @@ func main() { orderman.LoadPendingOrders() // 延时的原因是等回调准备好 - time.AfterFunc(2*time.Second, func() { - if err := api.DingDingAPI.RegisterCallback([]string{dingdingapi.CBTagUserAddOrg, dingdingapi.CBTagUserModifyOrg, dingdingapi.CBTagUserLeaveOrg}, beego.AppConfig.DefaultString("dingdingCallbackToken", ""), beego.AppConfig.DefaultString("dingdingCallbackAESKey", ""), beego.AppConfig.DefaultString("dingdingCallbackURL", "")); err != nil { - globals.SugarLogger.Warnf("dingding RegisterCallback failed with error:%v", err) - } - }) + if true { // beego.BConfig.RunMode == "prod" { + time.AfterFunc(2*time.Second, func() { + api.DingDingAPI.DeleteCallback() + if err := api.DingDingAPI.RegisterCallback([]string{dingdingapi.CBTagUserAddOrg, dingdingapi.CBTagUserModifyOrg, dingdingapi.CBTagUserLeaveOrg}, beego.AppConfig.DefaultString("dingdingCallbackToken", ""), beego.AppConfig.DefaultString("dingdingCallbackAESKey", ""), beego.AppConfig.DefaultString("dingdingCallbackURL", "")); err != nil { + globals.SugarLogger.Warnf("dingding RegisterCallback failed with error:%v", err) + } + }) + } if beego.BConfig.RunMode != "prod" { beego.BConfig.WebConfig.DirectoryIndex = true