From 3c359a047367744afb4a421ff4b85cf9f8780aee Mon Sep 17 00:00:00 2001 From: lwnmengjing Date: Mon, 30 Sep 2024 14:31:48 +0800 Subject: [PATCH] feat: support binding lark account --- apis/user.go | 18 ++++++ dto/user.go | 4 +- models/user.go | 157 ++++++++++++++++++++++++++----------------------- 3 files changed, 104 insertions(+), 75 deletions(-) diff --git a/apis/user.go b/apis/user.go index 19b4ccb..c1f4077 100644 --- a/apis/user.go +++ b/apis/user.go @@ -90,6 +90,8 @@ func (e *User) Binding(ctx *gin.Context) { switch req.Provider { case pkg.GithubLoginProvider: userOAuth2, err = user.GetUserGithubOAuth2(ctx) + case pkg.LarkLoginProvider: + userOAuth2, err = user.GetUserLarkOAuth2(ctx) default: api.Err(http.StatusNotImplemented) return @@ -158,11 +160,27 @@ func (e *User) GetOauth2(ctx *gin.Context) { // @Router /admin/api/user/reset-password [post] func (e *User) ResetPassword(ctx *gin.Context) { api := response.Make(ctx) + verify := response.VerifyHandler(ctx) req := &dto.ResetPasswordRequest{} if api.Bind(req).Error != nil { api.Err(http.StatusUnprocessableEntity) return } + if verify != nil { + err := models.PasswordReset(ctx, verify.GetUserID(), req.Password) + if err != nil { + api.AddError(err).Log.Error("PasswordReset error") + api.Err(http.StatusInternalServerError) + return + } + api.OK(nil) + return + } + if req.Email == "" || req.Captcha == "" { + api.Err(http.StatusForbidden) + return + + } ok, err := center.Default.VerifyCode(ctx, req.Email, req.Captcha) if err != nil { api.AddError(err).Log.Error("VerifyCode error") diff --git a/dto/user.go b/dto/user.go index 680856d..f6a55d7 100644 --- a/dto/user.go +++ b/dto/user.go @@ -23,8 +23,8 @@ type RegisterResponse struct { } type ResetPasswordRequest struct { - Email string `json:"email" binding:"required"` - Captcha string `json:"captcha" binding:"required"` + Email string `json:"email"` + Captcha string `json:"captcha"` Password string `json:"password" binding:"required"` } diff --git a/models/user.go b/models/user.go index fd203cb..e9acc0b 100644 --- a/models/user.go +++ b/models/user.go @@ -5,21 +5,19 @@ import ( "encoding/json" "errors" "fmt" - "github.com/sanity-io/litter" - "github.com/spf13/cast" "log/slog" "net/http" "strings" "time" - larkauthen "github.com/larksuite/oapi-sdk-go/v3/service/authen/v1" - "golang.org/x/oauth2" - "github.com/gin-gonic/gin" + larkauthen "github.com/larksuite/oapi-sdk-go/v3/service/authen/v1" corePKG "github.com/mss-boot-io/mss-boot/pkg" "github.com/mss-boot-io/mss-boot/pkg/config/gormdb" "github.com/mss-boot-io/mss-boot/pkg/enum" "github.com/mss-boot-io/mss-boot/pkg/security" + "github.com/spf13/cast" + "golang.org/x/oauth2" "gorm.io/gorm" "gorm.io/gorm/schema" @@ -266,7 +264,6 @@ func (e *UserLogin) Verify(ctx context.Context) (bool, security.Verifier, error) Avatar: userOAuth2.Picture, Profile: userOAuth2.Profile, } - litter.Dump(e) if e.GetUserID() != "" { userOAuth2.User = nil } @@ -278,83 +275,33 @@ func (e *UserLogin) Verify(ctx context.Context) (bool, security.Verifier, error) } return true, userOAuth2.User, nil case pkg.LarkLoginProvider: - client := http.Client{} - req, err := http.NewRequest(http.MethodGet, "https://open.larksuite.com/open-apis/authen/v1/user_info", nil) - if err != nil { - slog.Error("new request error", slog.Any("error", err)) - return false, nil, err - } - req.Header.Add("Authorization", "Bearer "+e.Password) - resp, err := client.Do(req) - if err != nil { - slog.Error("do request error", slog.Any("error", err)) - return false, nil, err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - err = errors.New("get user from lark error") - slog.Error(err.Error()) - return false, nil, err - } - result := &larkauthen.GetUserInfoResp{} - err = json.NewDecoder(resp.Body).Decode(result) + userOAuth2, err := e.GetUserLarkOAuth2(c) if err != nil { - slog.Error("decode response error", slog.Any("error", err)) + slog.Error("get user from lark error", slog.Any("error", err)) return false, nil, err } - userOAuth2 := &UserOAuth2{} - err = center.GetDB(ctx.(*gin.Context), &UserOAuth2{}). - Preload("User.Role"). - First(userOAuth2, "union_id = ?", result.Data.UnionId).Error - if err != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - slog.Error("get user from db error", slog.Any("error", err)) - return false, nil, err - } - err = nil - email := "" - if result.Data.EnterpriseEmail != nil { - email = *result.Data.EnterpriseEmail - } - if email == "" && result.Data.Email != nil { - email = *result.Data.Email - } - userOAuth2 = &UserOAuth2{ - UnionID: *result.Data.UnionId, - OpenID: *result.Data.OpenId, - Sub: *result.Data.TenantKey, - Name: *result.Data.Name, - Email: email, - Picture: *result.Data.AvatarUrl, - NickName: *result.Data.Name, - EmailVerified: email != "", - Provider: pkg.LarkLoginProvider, - User: &User{ - UserLogin: UserLogin{ - RoleID: defaultRole.ID, - Username: *result.Data.UserId, - Email: email, - Password: e.Password, - Provider: pkg.LarkLoginProvider, - Status: enum.Enabled, - }, - Name: *result.Data.Name, - Avatar: *result.Data.AvatarUrl, + if userOAuth2.ID == "" { + // register + userOAuth2.User = &User{ + UserLogin: UserLogin{ + RoleID: defaultRole.ID, + Username: userOAuth2.PreferredUsername, + Email: userOAuth2.Email, + Password: e.Password, + Provider: pkg.LarkLoginProvider, + Status: enum.Enabled, }, + Name: userOAuth2.Name, + Avatar: userOAuth2.Picture, } - if result.Data.Mobile != nil { - userOAuth2.PhoneNumber = *result.Data.Mobile - } - if result.Data.EmployeeNo != nil { - userOAuth2.EmployeeNO = *result.Data.EmployeeNo + if e.GetUserID() != "" { + userOAuth2.User = nil } - // register user - err = center.GetDB(c, &User{}).Create(userOAuth2).Error + err = center.GetDB(c, &UserOAuth2{}).Create(userOAuth2).Error if err != nil { slog.Error("create user error", slog.Any("error", err)) return false, nil, err } - userOAuth2.User.Role = defaultRole } return true, userOAuth2.User, nil case pkg.EmailLoginProvider: @@ -431,6 +378,70 @@ func (e *UserLogin) Verify(ctx context.Context) (bool, security.Verifier, error) return verify == user.PasswordHash, user, nil } +func (e *UserLogin) GetUserLarkOAuth2(c *gin.Context) (*UserOAuth2, error) { + client := http.Client{} + req, err := http.NewRequest(http.MethodGet, "https://open.larksuite.com/open-apis/authen/v1/user_info", nil) + if err != nil { + slog.Error("new request error", slog.Any("error", err)) + return nil, err + } + req.Header.Add("Authorization", "Bearer "+e.Password) + resp, err := client.Do(req) + if err != nil { + slog.Error("do request error", slog.Any("error", err)) + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + err = errors.New("get user from lark error") + slog.Error(err.Error()) + return nil, err + } + result := &larkauthen.GetUserInfoResp{} + err = json.NewDecoder(resp.Body).Decode(result) + if err != nil { + slog.Error("decode response error", slog.Any("error", err)) + return nil, err + } + userOAuth2 := &UserOAuth2{} + err = center.GetDB(c, &UserOAuth2{}). + Preload("User.Role"). + First(userOAuth2, "union_id = ?", result.Data.UnionId).Error + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + slog.Error("get user from db error", slog.Any("error", err)) + return nil, err + } + err = nil + email := "" + if result.Data.EnterpriseEmail != nil { + email = *result.Data.EnterpriseEmail + } + if email == "" && result.Data.Email != nil { + email = *result.Data.Email + } + userOAuth2 = &UserOAuth2{ + UnionID: *result.Data.UnionId, + OpenID: *result.Data.OpenId, + Sub: *result.Data.TenantKey, + Name: *result.Data.Name, + Email: email, + Picture: *result.Data.AvatarUrl, + NickName: *result.Data.Name, + EmailVerified: email != "", + Provider: pkg.LarkLoginProvider, + PreferredUsername: *result.Data.UserId, + } + if result.Data.Mobile != nil { + userOAuth2.PhoneNumber = *result.Data.Mobile + } + if result.Data.EmployeeNo != nil { + userOAuth2.EmployeeNO = *result.Data.EmployeeNo + } + } + return userOAuth2, nil +} + func (e *UserLogin) GetUserGithubOAuth2(c *gin.Context) (*UserOAuth2, error) { clientID, _ := center.GetAppConfig().GetAppConfig(c, "security.githubClientId") clientSecret, _ := center.GetAppConfig().GetAppConfig(c, "security.githubClientSecret")