limit the maximum count of password / token check failures per IP/user per minute (#33)
This commit is contained in:
@@ -180,6 +180,12 @@ email_verify_token_expired_time = 3600
|
||||
# Password reset token expired seconds (60 - 4294967295), default is 3600 (60 minutes)
|
||||
password_reset_token_expired_time = 3600
|
||||
|
||||
# Maximum count of password / token check failures (0 - 4294967295) per IP per minute (use the above duplicate checker), default is 5, set to 0 to disable
|
||||
max_failures_per_ip_per_minute = 5
|
||||
|
||||
# Maximum count of password / token check failures (0 - 4294967295) per user per minute (use the above duplicate checker), default is 5, set to 0 to disable
|
||||
max_failures_per_user_per_minute = 5
|
||||
|
||||
# Add X-Request-Id header to response to track user request or error, default is true
|
||||
request_id_header = true
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ var (
|
||||
container: settings.Container,
|
||||
},
|
||||
ApiUsingDuplicateChecker: ApiUsingDuplicateChecker{
|
||||
ApiUsingConfig: ApiUsingConfig{
|
||||
container: settings.Container,
|
||||
},
|
||||
container: duplicatechecker.Container,
|
||||
},
|
||||
accounts: services.Accounts,
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/avatars"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/duplicatechecker"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
// AuthorizationsApi represents authorization api
|
||||
type AuthorizationsApi struct {
|
||||
ApiUsingConfig
|
||||
ApiUsingDuplicateChecker
|
||||
ApiWithUserInfo
|
||||
users *services.UserService
|
||||
tokens *services.TokenService
|
||||
@@ -27,6 +29,12 @@ var (
|
||||
ApiUsingConfig: ApiUsingConfig{
|
||||
container: settings.Container,
|
||||
},
|
||||
ApiUsingDuplicateChecker: ApiUsingDuplicateChecker{
|
||||
ApiUsingConfig: ApiUsingConfig{
|
||||
container: settings.Container,
|
||||
},
|
||||
container: duplicatechecker.Container,
|
||||
},
|
||||
ApiWithUserInfo: ApiWithUserInfo{
|
||||
ApiUsingConfig: ApiUsingConfig{
|
||||
container: settings.Container,
|
||||
@@ -51,7 +59,23 @@ func (a *AuthorizationsApi) AuthorizeHandler(c *core.WebContext) (any, *errs.Err
|
||||
return nil, errs.ErrLoginNameOrPasswordInvalid
|
||||
}
|
||||
|
||||
user, err := a.users.GetUserByUsernameOrEmailAndPassword(c, credential.LoginName, credential.Password)
|
||||
err = a.CheckFailureCount(c, 0)
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[authorizations.AuthorizeHandler] cannot login for user \"%s\", because %s", credential.LoginName, err.Error())
|
||||
return nil, errs.Or(err, errs.ErrFailureCountLimitReached)
|
||||
}
|
||||
|
||||
user, uid, err := a.users.GetUserByUsernameOrEmailAndPassword(c, credential.LoginName, credential.Password)
|
||||
|
||||
if errs.IsCustomError(err) {
|
||||
failureCheckErr := a.CheckAndIncreaseFailureCount(c, uid)
|
||||
|
||||
if failureCheckErr != nil {
|
||||
log.Warnf(c, "[authorizations.AuthorizeHandler] cannot login for user \"%s\", because %s", credential.LoginName, failureCheckErr.Error())
|
||||
return nil, errs.Or(failureCheckErr, errs.ErrFailureCountLimitReached)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[authorizations.AuthorizeHandler] login failed for user \"%s\", because %s", credential.LoginName, err.Error())
|
||||
@@ -133,6 +157,13 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeHandler(c *core.WebContext) (any,
|
||||
}
|
||||
|
||||
uid := c.GetCurrentUid()
|
||||
err = a.CheckFailureCount(c, uid)
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[authorizations.TwoFactorAuthorizeHandler] cannot auth for user \"uid:%d\", because %s", uid, err.Error())
|
||||
return nil, errs.Or(err, errs.ErrFailureCountLimitReached)
|
||||
}
|
||||
|
||||
twoFactorSetting, err := a.twoFactorAuthorizations.GetUserTwoFactorSettingByUid(c, uid)
|
||||
|
||||
if err != nil {
|
||||
@@ -142,6 +173,14 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeHandler(c *core.WebContext) (any,
|
||||
|
||||
if !totp.Validate(credential.Passcode, twoFactorSetting.Secret) {
|
||||
log.Warnf(c, "[authorizations.TwoFactorAuthorizeHandler] passcode is invalid for user \"uid:%d\"", uid)
|
||||
|
||||
err = a.CheckAndIncreaseFailureCount(c, uid)
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[authorizations.TwoFactorAuthorizeHandler] cannot auth for user \"uid:%d\", because %s", uid, err.Error())
|
||||
return nil, errs.Or(err, errs.ErrFailureCountLimitReached)
|
||||
}
|
||||
|
||||
return nil, errs.ErrPasscodeInvalid
|
||||
}
|
||||
|
||||
@@ -196,6 +235,13 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeByRecoveryCodeHandler(c *core.WebC
|
||||
}
|
||||
|
||||
uid := c.GetCurrentUid()
|
||||
err = a.CheckFailureCount(c, uid)
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] cannot auth for user \"uid:%d\", because %s", uid, err.Error())
|
||||
return nil, errs.Or(err, errs.ErrFailureCountLimitReached)
|
||||
}
|
||||
|
||||
enableTwoFactor, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(c, uid)
|
||||
|
||||
if err != nil {
|
||||
@@ -226,6 +272,15 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeByRecoveryCodeHandler(c *core.WebC
|
||||
|
||||
err = a.twoFactorAuthorizations.GetAndUseUserTwoFactorRecoveryCode(c, uid, credential.RecoveryCode, user.Salt)
|
||||
|
||||
if errs.IsCustomError(err) {
|
||||
failureCheckErr := a.CheckAndIncreaseFailureCount(c, uid)
|
||||
|
||||
if failureCheckErr != nil {
|
||||
log.Warnf(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] cannot auth for user \"uid:%d\", because %s", uid, failureCheckErr.Error())
|
||||
return nil, errs.Or(failureCheckErr, errs.ErrFailureCountLimitReached)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Warnf(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to get two-factor recovery code for user \"uid:%d\", because %s", uid, err.Error())
|
||||
return nil, errs.Or(err, errs.ErrTwoFactorRecoveryCodeNotExist)
|
||||
|
||||
@@ -5,9 +5,13 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/avatars"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/duplicatechecker"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||
)
|
||||
|
||||
const internalTransactionPictureUrlFormat = "%spictures/%d.%s"
|
||||
@@ -100,6 +104,7 @@ func (a *ApiUsingConfig) GetAfterOpenNotificationContent(userLanguage string, cl
|
||||
|
||||
// ApiUsingDuplicateChecker represents an api that need to use duplicate checker
|
||||
type ApiUsingDuplicateChecker struct {
|
||||
ApiUsingConfig
|
||||
container *duplicatechecker.DuplicateCheckerContainer
|
||||
}
|
||||
|
||||
@@ -113,6 +118,67 @@ func (a *ApiUsingDuplicateChecker) SetSubmissionRemark(checkerType duplicatechec
|
||||
a.container.SetSubmissionRemark(checkerType, uid, identification, remark)
|
||||
}
|
||||
|
||||
// CheckFailureCount returns whether the failure count of the specified IP and user has reached the limit and increases the failure count
|
||||
func (a *ApiUsingDuplicateChecker) CheckFailureCount(c *core.WebContext, uid int64) error {
|
||||
if a.CurrentConfig().MaxFailuresPerIpPerMinute > 0 {
|
||||
clientIp := c.ClientIP()
|
||||
ipFailureCount := a.container.GetFailureCount(clientIp)
|
||||
|
||||
if ipFailureCount >= a.CurrentConfig().MaxFailuresPerIpPerMinute {
|
||||
log.Warnf(c, "[base.CheckFailureCount] operation failure via IP \"%s\", current failure count: %d reached the limit", clientIp, ipFailureCount)
|
||||
return errs.ErrFailureCountLimitReached
|
||||
}
|
||||
}
|
||||
|
||||
if a.CurrentConfig().MaxFailuresPerUserPerMinute > 0 && uid > 0 {
|
||||
uidFailureCount := a.container.GetFailureCount(utils.Int64ToString(uid))
|
||||
|
||||
if uidFailureCount >= a.CurrentConfig().MaxFailuresPerUserPerMinute {
|
||||
log.Warnf(c, "[base.CheckFailureCount] operation failure via uid \"%d\", current failure count: %d reached the limit", uid, uidFailureCount)
|
||||
return errs.ErrFailureCountLimitReached
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckAndIncreaseFailureCount returns whether the failure count of the specified IP and user has reached the limit and increases the failure count
|
||||
func (a *ApiUsingDuplicateChecker) CheckAndIncreaseFailureCount(c *core.WebContext, uid int64) error {
|
||||
clientIp := c.ClientIP()
|
||||
ipFailureCount := uint32(0)
|
||||
uidFailureCount := uint32(0)
|
||||
|
||||
if a.CurrentConfig().MaxFailuresPerIpPerMinute > 0 {
|
||||
ipFailureCount = a.container.GetFailureCount(clientIp)
|
||||
}
|
||||
|
||||
if a.CurrentConfig().MaxFailuresPerUserPerMinute > 0 && uid > 0 {
|
||||
uidFailureCount = a.container.GetFailureCount(utils.Int64ToString(uid))
|
||||
}
|
||||
|
||||
if a.CurrentConfig().MaxFailuresPerIpPerMinute > 0 && ipFailureCount < a.CurrentConfig().MaxFailuresPerIpPerMinute {
|
||||
log.Warnf(c, "[base.CheckAndIncreaseFailureCount] operation failure via IP \"%s\", previous failure count: %d", clientIp, ipFailureCount)
|
||||
a.container.IncreaseFailureCount(clientIp)
|
||||
}
|
||||
|
||||
if a.CurrentConfig().MaxFailuresPerUserPerMinute > 0 && uid > 0 && uidFailureCount < a.CurrentConfig().MaxFailuresPerUserPerMinute {
|
||||
log.Warnf(c, "[base.CheckAndIncreaseFailureCount] operation failure via uid \"%d\", previous failure count: %d", uid, uidFailureCount)
|
||||
a.container.IncreaseFailureCount(utils.Int64ToString(uid))
|
||||
}
|
||||
|
||||
if a.CurrentConfig().MaxFailuresPerIpPerMinute > 0 && ipFailureCount >= a.CurrentConfig().MaxFailuresPerIpPerMinute {
|
||||
log.Warnf(c, "[base.CheckAndIncreaseFailureCount] operation failure via IP \"%s\", current failure count: %d reached the limit", clientIp, ipFailureCount)
|
||||
return errs.ErrFailureCountLimitReached
|
||||
}
|
||||
|
||||
if a.CurrentConfig().MaxFailuresPerUserPerMinute > 0 && uid > 0 && uidFailureCount >= a.CurrentConfig().MaxFailuresPerUserPerMinute {
|
||||
log.Warnf(c, "[base.CheckAndIncreaseFailureCount] operation failure via uid \"%d\", current failure count: %d reached the limit", uid, uidFailureCount)
|
||||
return errs.ErrFailureCountLimitReached
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApiUsingAvatarProvider represents an api that need to use avatar provider
|
||||
type ApiUsingAvatarProvider struct {
|
||||
container *avatars.AvatarProviderContainer
|
||||
|
||||
@@ -29,6 +29,9 @@ var (
|
||||
container: settings.Container,
|
||||
},
|
||||
ApiUsingDuplicateChecker: ApiUsingDuplicateChecker{
|
||||
ApiUsingConfig: ApiUsingConfig{
|
||||
container: settings.Container,
|
||||
},
|
||||
container: duplicatechecker.Container,
|
||||
},
|
||||
categories: services.TransactionCategories,
|
||||
|
||||
@@ -26,6 +26,9 @@ var (
|
||||
container: settings.Container,
|
||||
},
|
||||
ApiUsingDuplicateChecker: ApiUsingDuplicateChecker{
|
||||
ApiUsingConfig: ApiUsingConfig{
|
||||
container: settings.Container,
|
||||
},
|
||||
container: duplicatechecker.Container,
|
||||
},
|
||||
users: services.Users,
|
||||
|
||||
@@ -31,6 +31,9 @@ var (
|
||||
container: settings.Container,
|
||||
},
|
||||
ApiUsingDuplicateChecker: ApiUsingDuplicateChecker{
|
||||
ApiUsingConfig: ApiUsingConfig{
|
||||
container: settings.Container,
|
||||
},
|
||||
container: duplicatechecker.Container,
|
||||
},
|
||||
templates: services.TransactionTemplates,
|
||||
|
||||
@@ -43,6 +43,9 @@ var (
|
||||
container: settings.Container,
|
||||
},
|
||||
ApiUsingDuplicateChecker: ApiUsingDuplicateChecker{
|
||||
ApiUsingConfig: ApiUsingConfig{
|
||||
container: settings.Container,
|
||||
},
|
||||
container: duplicatechecker.Container,
|
||||
},
|
||||
transactions: services.Transactions,
|
||||
|
||||
@@ -8,4 +8,6 @@ type DuplicateChecker interface {
|
||||
SetSubmissionRemark(checkerType DuplicateCheckerType, uid int64, identification string, remark string)
|
||||
GetOrSetCronJobRunningInfo(jobName string, runningInfo string, runningInterval time.Duration) (bool, string)
|
||||
RemoveCronJobRunningInfo(jobName string)
|
||||
GetFailureCount(failureKey string) uint32
|
||||
IncreaseFailureCount(failureKey string) uint32
|
||||
}
|
||||
|
||||
@@ -48,3 +48,13 @@ func (c *DuplicateCheckerContainer) GetOrSetCronJobRunningInfo(jobName string, r
|
||||
func (c *DuplicateCheckerContainer) RemoveCronJobRunningInfo(jobName string) {
|
||||
c.Current.RemoveCronJobRunningInfo(jobName)
|
||||
}
|
||||
|
||||
// GetFailureCount returns the failure count of the specified failure key
|
||||
func (c *DuplicateCheckerContainer) GetFailureCount(failureKey string) uint32 {
|
||||
return c.Current.GetFailureCount(failureKey)
|
||||
}
|
||||
|
||||
// IncreaseFailureCount increases the failure count of the specified failure key
|
||||
func (c *DuplicateCheckerContainer) IncreaseFailureCount(failureKey string) uint32 {
|
||||
return c.Current.IncreaseFailureCount(failureKey)
|
||||
}
|
||||
|
||||
@@ -12,4 +12,5 @@ const (
|
||||
DUPLICATE_CHECKER_TYPE_NEW_TEMPLATE DuplicateCheckerType = 4
|
||||
DUPLICATE_CHECKER_TYPE_NEW_PICTURE DuplicateCheckerType = 5
|
||||
DUPLICATE_CHECKER_TYPE_IMPORT_TRANSACTIONS DuplicateCheckerType = 6
|
||||
DUPLICATE_CHECKER_TYPE_FAILURE_CHECK DuplicateCheckerType = 255
|
||||
)
|
||||
|
||||
@@ -69,6 +69,34 @@ func (c *InMemoryDuplicateChecker) RemoveCronJobRunningInfo(jobName string) {
|
||||
c.cache.Delete(c.getCacheKey(DUPLICATE_CHECKER_TYPE_BACKGROUND_CRON_JOB, 0, jobName))
|
||||
}
|
||||
|
||||
// GetFailureCount returns the failure count of the specified failure key
|
||||
func (c *InMemoryDuplicateChecker) GetFailureCount(failureKey string) uint32 {
|
||||
existedFailureCount, found := c.cache.Get(c.getCacheKey(DUPLICATE_CHECKER_TYPE_FAILURE_CHECK, 0, failureKey))
|
||||
|
||||
if found {
|
||||
return existedFailureCount.(uint32)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// IncreaseFailureCount increases the failure count of the specified failure key
|
||||
func (c *InMemoryDuplicateChecker) IncreaseFailureCount(failureKey string) uint32 {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
cacheKey := c.getCacheKey(DUPLICATE_CHECKER_TYPE_FAILURE_CHECK, 0, failureKey)
|
||||
_, found := c.cache.Get(cacheKey)
|
||||
|
||||
if found {
|
||||
failureCount, _ := c.cache.IncrementUint32(cacheKey, uint32(1))
|
||||
return failureCount
|
||||
} else {
|
||||
c.cache.Set(cacheKey, uint32(1), 1*time.Minute)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
func (c *InMemoryDuplicateChecker) getCacheKey(checkerType DuplicateCheckerType, uid int64, identification string) string {
|
||||
return fmt.Sprintf("%d|%d|%s", checkerType, uid, identification)
|
||||
}
|
||||
|
||||
@@ -155,3 +155,77 @@ func TestGetOrSetRunningInfoConcurrent(t *testing.T) {
|
||||
|
||||
assert.Equal(t, uint32(999), setRunningInfoCount.Load())
|
||||
}
|
||||
|
||||
func TestGetFailureCount(t *testing.T) {
|
||||
checker, _ := NewInMemoryDuplicateChecker(&settings.Config{
|
||||
DuplicateSubmissionsIntervalDuration: time.Second,
|
||||
InMemoryDuplicateCheckerCleanupIntervalDuration: time.Second,
|
||||
})
|
||||
|
||||
failureKey := "127.0.0.1"
|
||||
|
||||
failureCount := checker.GetFailureCount(failureKey)
|
||||
assert.Equal(t, uint32(0), failureCount)
|
||||
|
||||
failureCount = checker.IncreaseFailureCount(failureKey)
|
||||
assert.Equal(t, uint32(1), failureCount)
|
||||
|
||||
failureCount = checker.GetFailureCount(failureKey)
|
||||
assert.Equal(t, uint32(1), failureCount)
|
||||
}
|
||||
|
||||
func TestIncreaseFailureCount(t *testing.T) {
|
||||
checker, _ := NewInMemoryDuplicateChecker(&settings.Config{
|
||||
DuplicateSubmissionsIntervalDuration: time.Second,
|
||||
InMemoryDuplicateCheckerCleanupIntervalDuration: time.Second,
|
||||
})
|
||||
|
||||
failureKey := "127.0.0.1"
|
||||
|
||||
failureCount := checker.IncreaseFailureCount(failureKey)
|
||||
assert.Equal(t, uint32(1), failureCount)
|
||||
|
||||
failureCount = checker.GetFailureCount(failureKey)
|
||||
assert.Equal(t, uint32(1), failureCount)
|
||||
|
||||
failureCount = checker.IncreaseFailureCount(failureKey)
|
||||
assert.Equal(t, uint32(2), failureCount)
|
||||
|
||||
failureCount = checker.GetFailureCount(failureKey)
|
||||
assert.Equal(t, uint32(2), failureCount)
|
||||
|
||||
failureCount = checker.IncreaseFailureCount(failureKey)
|
||||
assert.Equal(t, uint32(3), failureCount)
|
||||
|
||||
failureCount = checker.GetFailureCount(failureKey)
|
||||
assert.Equal(t, uint32(3), failureCount)
|
||||
}
|
||||
|
||||
func TestIncreaseFailureCountConcurrent(t *testing.T) {
|
||||
checker, _ := NewInMemoryDuplicateChecker(&settings.Config{
|
||||
DuplicateSubmissionsIntervalDuration: time.Second,
|
||||
InMemoryDuplicateCheckerCleanupIntervalDuration: time.Second,
|
||||
})
|
||||
|
||||
failureKey := "127.0.0.1"
|
||||
|
||||
concurrentCount := 10
|
||||
var waitGroup sync.WaitGroup
|
||||
|
||||
for routineIndex := 0; routineIndex < concurrentCount; routineIndex++ {
|
||||
waitGroup.Add(1)
|
||||
|
||||
go func(currentRoutineIndex int) {
|
||||
for cycle := 0; cycle < 10; cycle++ {
|
||||
checker.IncreaseFailureCount(failureKey)
|
||||
}
|
||||
|
||||
waitGroup.Done()
|
||||
}(routineIndex)
|
||||
}
|
||||
|
||||
waitGroup.Wait()
|
||||
|
||||
failureCount := checker.GetFailureCount(failureKey)
|
||||
assert.Equal(t, uint32(100), failureCount)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ var (
|
||||
ErrNoFilesUpload = NewNormalError(NormalSubcategoryGlobal, 15, http.StatusBadRequest, "no files uploaded")
|
||||
ErrUploadedFileEmpty = NewNormalError(NormalSubcategoryGlobal, 16, http.StatusBadRequest, "uploaded file is empty")
|
||||
ErrExceedMaxUploadFileSize = NewNormalError(NormalSubcategoryGlobal, 17, http.StatusBadRequest, "uploaded file size exceeds the maximum allowed size")
|
||||
ErrFailureCountLimitReached = NewNormalError(NormalSubcategoryGlobal, 18, http.StatusBadRequest, "failure count exceeded maximum limit")
|
||||
)
|
||||
|
||||
// GetParameterInvalidMessage returns specific error message for invalid parameter error
|
||||
|
||||
@@ -58,7 +58,7 @@ var (
|
||||
)
|
||||
|
||||
// GetUserByUsernameOrEmailAndPassword returns the user model according to login name and password
|
||||
func (s *UserService) GetUserByUsernameOrEmailAndPassword(c core.Context, loginname string, password string) (*models.User, error) {
|
||||
func (s *UserService) GetUserByUsernameOrEmailAndPassword(c core.Context, loginname string, password string) (*models.User, int64, error) {
|
||||
var user *models.User
|
||||
var err error
|
||||
|
||||
@@ -71,14 +71,18 @@ func (s *UserService) GetUserByUsernameOrEmailAndPassword(c core.Context, loginn
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
return nil, 0, errs.ErrUserNotFound
|
||||
}
|
||||
|
||||
if !s.IsPasswordEqualsUserPassword(password, user) {
|
||||
return nil, errs.ErrUserPasswordWrong
|
||||
return nil, user.Uid, errs.ErrUserPasswordWrong
|
||||
}
|
||||
|
||||
return user, nil
|
||||
return user, user.Uid, nil
|
||||
}
|
||||
|
||||
// GetUserById returns the user model according to user uid
|
||||
|
||||
@@ -144,6 +144,8 @@ const (
|
||||
defaultTemporaryTokenExpiredTime uint32 = 300 // 5 minutes
|
||||
defaultEmailVerifyTokenExpiredTime uint32 = 3600 // 60 minutes
|
||||
defaultPasswordResetTokenExpiredTime uint32 = 3600 // 60 minutes
|
||||
defaultMaxFailuresPerIpPerMinute uint32 = 5
|
||||
defaultMaxFailuresPerUserPerMinute uint32 = 5
|
||||
|
||||
defaultTransactionPictureFileMaxSize uint32 = 10485760 // 10MB
|
||||
defaultUserAvatarFileMaxSize uint32 = 1048576 // 1MB
|
||||
@@ -286,6 +288,8 @@ type Config struct {
|
||||
EmailVerifyTokenExpiredTimeDuration time.Duration
|
||||
PasswordResetTokenExpiredTime uint32
|
||||
PasswordResetTokenExpiredTimeDuration time.Duration
|
||||
MaxFailuresPerIpPerMinute uint32
|
||||
MaxFailuresPerUserPerMinute uint32
|
||||
EnableRequestIdHeader bool
|
||||
|
||||
// User
|
||||
@@ -768,6 +772,9 @@ func loadSecurityConfiguration(config *Config, configFile *ini.File, sectionName
|
||||
|
||||
config.PasswordResetTokenExpiredTimeDuration = time.Duration(config.PasswordResetTokenExpiredTime) * time.Second
|
||||
|
||||
config.MaxFailuresPerIpPerMinute = getConfigItemUint32Value(configFile, sectionName, "max_failures_per_ip_per_minute", defaultMaxFailuresPerIpPerMinute)
|
||||
config.MaxFailuresPerUserPerMinute = getConfigItemUint32Value(configFile, sectionName, "max_failures_per_user_per_minute", defaultMaxFailuresPerUserPerMinute)
|
||||
|
||||
config.EnableRequestIdHeader = getConfigItemBoolValue(configFile, sectionName, "request_id_header", true)
|
||||
|
||||
return nil
|
||||
|
||||
+2
-1
@@ -1167,7 +1167,8 @@
|
||||
"number invalid": "Nummer ist ungültig",
|
||||
"no files uploaded": "Keine Dateien hochgeladen",
|
||||
"uploaded file is empty": "Hochgeladene Datei ist leer",
|
||||
"uploaded file size exceeds the maximum allowed size": "Hochgeladene Datei überschreitet die maximal zulässige Größe"
|
||||
"uploaded file size exceeds the maximum allowed size": "Hochgeladene Datei überschreitet die maximal zulässige Größe",
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "ID",
|
||||
|
||||
+2
-1
@@ -1167,7 +1167,8 @@
|
||||
"number invalid": "Number is invalid",
|
||||
"no files uploaded": "No files uploaded",
|
||||
"uploaded file is empty": "Uploaded file is empty",
|
||||
"uploaded file size exceeds the maximum allowed size": "Uploaded file size exceeds the maximum allowed size"
|
||||
"uploaded file size exceeds the maximum allowed size": "Uploaded file size exceeds the maximum allowed size",
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "ID",
|
||||
|
||||
+2
-1
@@ -1167,7 +1167,8 @@
|
||||
"number invalid": "El número no es válido",
|
||||
"no files uploaded": "No se subieron archivos",
|
||||
"uploaded file is empty": "El archivo subido está vacío",
|
||||
"uploaded file size exceeds the maximum allowed size": "El tamaño del archivo cargado excede el tamaño máximo permitido"
|
||||
"uploaded file size exceeds the maximum allowed size": "El tamaño del archivo cargado excede el tamaño máximo permitido",
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "IDENTIFICACIÓN",
|
||||
|
||||
+2
-1
@@ -1167,7 +1167,8 @@
|
||||
"number invalid": "番号が無効です",
|
||||
"no files uploaded": "アップロードされたファイルはありません",
|
||||
"uploaded file is empty": "アップロードされたファイルは空です",
|
||||
"uploaded file size exceeds the maximum allowed size": "アップロードされたファイルが最大許容サイズを超えています"
|
||||
"uploaded file size exceeds the maximum allowed size": "アップロードされたファイルが最大許容サイズを超えています",
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "ID",
|
||||
|
||||
+2
-1
@@ -1167,7 +1167,8 @@
|
||||
"number invalid": "Число недействительно",
|
||||
"no files uploaded": "Файлы не загружены",
|
||||
"uploaded file is empty": "Загруженный файл пуст",
|
||||
"uploaded file size exceeds the maximum allowed size": "Размер загруженного файла превышает максимально допустимый размер"
|
||||
"uploaded file size exceeds the maximum allowed size": "Размер загруженного файла превышает максимально допустимый размер",
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "ID",
|
||||
|
||||
+2
-1
@@ -1167,7 +1167,8 @@
|
||||
"number invalid": "Số không hợp lệ",
|
||||
"no files uploaded": "Không có tệp nào được tải lên",
|
||||
"uploaded file is empty": "Tệp đã tải lên trống",
|
||||
"uploaded file size exceeds the maximum allowed size": "Kích thước tệp đã tải lên vượt quá kích thước tối đa cho phép"
|
||||
"uploaded file size exceeds the maximum allowed size": "Kích thước tệp đã tải lên vượt quá kích thước tối đa cho phép",
|
||||
"failure count exceeded maximum limit": "Failure count exceeded maximum limit, please try again after some time"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "ID",
|
||||
|
||||
@@ -1167,7 +1167,8 @@
|
||||
"number invalid": "数字错误",
|
||||
"no files uploaded": "没有上传文件",
|
||||
"uploaded file is empty": "上传的文件为空",
|
||||
"uploaded file size exceeds the maximum allowed size": "上传的文件大小超出了允许的最大大小"
|
||||
"uploaded file size exceeds the maximum allowed size": "上传的文件大小超出了允许的最大大小",
|
||||
"failure count exceeded maximum limit": "失败次数超出最大限制,请稍后重试"
|
||||
},
|
||||
"parameter": {
|
||||
"id": "ID",
|
||||
|
||||
Reference in New Issue
Block a user