feat(login): add login limit (#1023)

* feat(login): add login limit

* chore: rename vars

* chore: use `string`

* fix: clear the signin error times after succeessfull login

* chore: modify code position
This commit is contained in:
q1anx1 2022-08-17 01:39:53 +08:00 committed by GitHub
parent 70f2988f09
commit aedef1eea1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 73 additions and 2 deletions

View File

@ -18,6 +18,7 @@ import (
"fmt"
"regexp"
"strings"
"time"
"unicode"
"github.com/casdoor/casdoor/cred"
@ -30,6 +31,11 @@ var (
reFieldWhiteList *regexp.Regexp
)
const (
SigninWrongTimesLimit = 5
LastSignWrongTimeDuration = time.Minute * 15
)
func init() {
reWhiteSpace, _ = regexp.Compile(`\s`)
reFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`)
@ -127,7 +133,32 @@ func CheckUserSignup(application *Application, organization *Organization, usern
return ""
}
func checkSigninErrorTimes(user *User) string {
if user.SigninWrongTimes >= SigninWrongTimesLimit {
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
passedTime := time.Now().UTC().Sub(lastSignWrongTime)
seconds := int(LastSignWrongTimeDuration.Seconds() - passedTime.Seconds())
// deny the login if the error times is greater than the limit and the last login time is less than the duration
if seconds > 0 {
return fmt.Sprintf("You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again", seconds/60, seconds%60)
}
// reset the error times
user.SigninWrongTimes = 0
UpdateUser(user.GetId(), user, []string{"signin_wrong_times"}, user.IsGlobalAdmin)
}
return ""
}
func CheckPassword(user *User, password string) string {
// check the login error times
if msg := checkSigninErrorTimes(user); msg != "" {
return msg
}
organization := GetOrganizationByUser(user)
if organization == nil {
return "organization does not exist"
@ -137,14 +168,17 @@ func CheckPassword(user *User, password string) string {
if credManager != nil {
if organization.MasterPassword != "" {
if credManager.IsPasswordCorrect(password, organization.MasterPassword, "", organization.PasswordSalt) {
resetUserSigninErrorTimes(user)
return ""
}
}
if credManager.IsPasswordCorrect(password, user.Password, user.PasswordSalt, organization.PasswordSalt) {
resetUserSigninErrorTimes(user)
return ""
}
return "password incorrect"
return recordSigninErrorInfo(user)
} else {
return fmt.Sprintf("unsupported password type: %s", organization.PasswordType)
}

View File

@ -14,7 +14,11 @@
package object
import "regexp"
import (
"fmt"
"regexp"
"time"
)
var reRealName *regexp.Regexp
@ -29,3 +33,32 @@ func init() {
func isValidRealName(s string) bool {
return reRealName.MatchString(s)
}
func resetUserSigninErrorTimes(user *User) {
// if the password is correct and wrong times is not zero, reset the error times
if user.SigninWrongTimes == 0 {
return
}
user.SigninWrongTimes = 0
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin)
}
func recordSigninErrorInfo(user *User) string {
// increase failed login count
user.SigninWrongTimes++
if user.SigninWrongTimes >= SigninWrongTimesLimit {
// record the latest failed login time
user.LastSigninWrongTime = time.Now().UTC().Format(time.RFC3339)
}
// update user
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin)
leftChances := SigninWrongTimesLimit - user.SigninWrongTimes
if leftChances > 0 {
return fmt.Sprintf("password is incorrect, you have %d remaining chances", leftChances)
}
// don't show the chance error message if the user has no chance left
return fmt.Sprintf("You have entered the wrong password too many times, please wait for %d minutes and try again", int(LastSignWrongTimeDuration.Minutes()))
}

View File

@ -111,6 +111,9 @@ type User struct {
Roles []*Role `json:"roles"`
Permissions []*Permission `json:"permissions"`
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
SigninWrongTimes int `json:"signinWrongTimes"`
}
type Userinfo struct {
@ -376,6 +379,7 @@ func UpdateUser(id string, user *User, columns []string, isGlobalAdmin bool) boo
"owner", "display_name", "avatar",
"location", "address", "region", "language", "affiliation", "title", "homepage", "bio", "score", "tag", "signup_application",
"is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials",
"signin_wrong_times", "last_signin_wrong_time",
}
}
if isGlobalAdmin {