diff --git a/cmd/dashboard/controller/jwt.go b/cmd/dashboard/controller/jwt.go index f1e217b..2adf757 100644 --- a/cmd/dashboard/controller/jwt.go +++ b/cmd/dashboard/controller/jwt.go @@ -89,6 +89,7 @@ func authenticator() func(c *gin.Context) (interface{}, error) { var user model.User realip := c.GetString(model.CtxKeyRealIPStr) + if err := singleton.DB.Select("id", "password").Where("username = ?", loginVals.Username).First(&user).Error; err != nil { if err == gorm.ErrRecordNotFound { model.BlockIP(singleton.DB, realip, model.WAFBlockReasonTypeLoginFail, model.BlockIDUnknownUser) @@ -96,6 +97,11 @@ func authenticator() func(c *gin.Context) (interface{}, error) { return nil, jwt.ErrFailedAuthentication } + if user.RejectPassword { + model.BlockIP(singleton.DB, realip, model.WAFBlockReasonTypeLoginFail, int64(user.ID)) + return nil, jwt.ErrForbidden + } + if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(loginVals.Password)); err != nil { model.BlockIP(singleton.DB, realip, model.WAFBlockReasonTypeLoginFail, int64(user.ID)) return nil, jwt.ErrFailedAuthentication diff --git a/cmd/dashboard/controller/oauth2.go b/cmd/dashboard/controller/oauth2.go index 070e6c0..e238ec5 100644 --- a/cmd/dashboard/controller/oauth2.go +++ b/cmd/dashboard/controller/oauth2.go @@ -42,7 +42,7 @@ func oauth2redirect(c *gin.Context) (*model.Oauth2LoginResponse, error) { return nil, singleton.Localizer.ErrorT("provider is required") } - rTypeInt, err := strconv.Atoi(c.Query("type")) + rTypeInt, err := strconv.ParseUint(c.Query("type"), 10, 8) if err != nil { return nil, err } @@ -87,10 +87,23 @@ func unbindOauth2(c *gin.Context) (any, error) { return nil, singleton.Localizer.ErrorT("provider not found") } provider = strings.ToLower(provider) + u := c.MustGet(model.CtxKeyAuthorizedUser).(*model.User) - if err := singleton.DB.Where("provider = ? AND user_id = ?", provider, u.ID).Delete(&model.Oauth2Bind{}).Error; err != nil { + query := singleton.DB.Where("provider = ? AND user_id = ?", provider, u.ID) + + var bindCount int64 + if err := query.Model(&model.Oauth2Bind{}).Count(&bindCount).Error; err != nil { return nil, newGormError("%v", err) } + + if bindCount < 2 && u.RejectPassword { + return nil, singleton.Localizer.ErrorT("operation not permitted") + } + + if err := query.Delete(&model.Oauth2Bind{}).Error; err != nil { + return nil, newGormError("%v", err) + } + return nil, nil } diff --git a/cmd/dashboard/controller/user.go b/cmd/dashboard/controller/user.go index 1a0f7ae..44f7577 100644 --- a/cmd/dashboard/controller/user.go +++ b/cmd/dashboard/controller/user.go @@ -73,8 +73,18 @@ func updateProfile(c *gin.Context) (any, error) { return nil, err } + var bindCount int64 + if err := singleton.DB.Where("user_id = ?", auth.(*model.User).ID).Count(&bindCount).Error; err != nil { + return nil, newGormError("%v", err) + } + + if pf.RejectPassword && bindCount < 1 { + return nil, singleton.Localizer.ErrorT("you don't have any oauth2 bindings") + } + user.Username = pf.NewUsername user.Password = string(hash) + user.RejectPassword = pf.RejectPassword if err := singleton.DB.Save(&user).Error; err != nil { return nil, newGormError("%v", err) } diff --git a/model/user.go b/model/user.go index 24c1613..9e214a5 100644 --- a/model/user.go +++ b/model/user.go @@ -15,10 +15,11 @@ const ( type User struct { Common - Username string `json:"username,omitempty" gorm:"uniqueIndex"` - Password string `json:"password,omitempty" gorm:"type:char(72)"` - Role uint8 `json:"role,omitempty"` - AgentSecret string `json:"agent_secret,omitempty" gorm:"type:char(32)"` + Username string `json:"username,omitempty" gorm:"uniqueIndex"` + Password string `json:"password,omitempty" gorm:"type:char(72)"` + Role uint8 `json:"role,omitempty"` + AgentSecret string `json:"agent_secret,omitempty" gorm:"type:char(32)"` + RejectPassword bool `json:"reject_password,omitempty"` } type UserInfo struct { diff --git a/model/user_api.go b/model/user_api.go index 62219b5..315fb50 100644 --- a/model/user_api.go +++ b/model/user_api.go @@ -10,4 +10,5 @@ type ProfileForm struct { OriginalPassword string `json:"original_password,omitempty"` NewUsername string `json:"new_username,omitempty"` NewPassword string `json:"new_password,omitempty"` + RejectPassword bool `json:"reject_password,omitempty"` }