feat(keystone): more specific error return during authentication

add error class UserNotFound, UserLocked, UserDisabled and WrongPassword
This commit is contained in:
rainzm
2021-01-27 19:30:24 +08:00
parent ed33cd4464
commit 1c325f747d
6 changed files with 40 additions and 19 deletions
+9 -4
View File
@@ -30,7 +30,6 @@ const (
ErrSpecNotFound = errors.Error("SpecNotFoundError")
ErrActionNotFound = errors.Error("ActionNotFoundError")
ErrTenantNotFound = errors.Error("TenantNotFoundError")
ErrUserNotFound = errors.Error("UserNotFoundError")
ErrServerStatus = errors.Error("ServerStatusError")
ErrInvalidStatus = errors.ErrInvalidStatus
@@ -84,12 +83,15 @@ const (
ErrTooManyAttempts = errors.Error("TooManyFailedAttempts")
ErrTooManyRequests = errors.Error("TooManyRequests")
ErrUserLocked = errors.Error("User Locked")
ErrUserDisabled = errors.Error("User Disabled")
ErrUnsupportedProtocol = errors.Error("UnsupportedProtocol")
ErrPolicyDefinition = errors.Error("PolicyDefinitionError")
ErrUserNotFound = errors.Error("UserNotFound")
ErrUserLocked = errors.Error("UserLocked")
ErrUserDisabled = errors.Error("UserDisabled")
ErrWrongPassword = errors.Error("WrongPassword")
ErrIncorrectUsernameOrPassword = errors.Error("IncorrectUsernameOrPassword")
)
var (
@@ -166,6 +168,9 @@ var (
ErrUserLocked: 423,
ErrUserDisabled: 423,
ErrWrongPassword: 401,
ErrIncorrectUsernameOrPassword: 401,
ErrPolicyDefinition: 409,
}
)
+8
View File
@@ -207,3 +207,11 @@ func NewUnclassifiedError(msg string, params ...interface{}) *httputils.JSONClie
func NewTooLargeEntityError(msg string, params ...interface{}) *httputils.JSONClientError {
return httputils.NewJsonClientError(httpErrorCode[ErrTooLarge], string(ErrTooLarge), msg, params...)
}
func NewJsonClientError(err errors.Error, msg string, params ...interface{}) *httputils.JSONClientError {
code, ok := httpErrorCode[err]
if !ok {
code = 500
}
return httputils.NewJsonClientError(code, string(err), msg, params...)
}
+6
View File
@@ -294,6 +294,12 @@ func (self *SLDAPDriver) Authenticate(ctx context.Context, ident mcclient.SAuthe
)
if err != nil {
log.Errorf("LDAP AUTH error: %s", err)
if errors.Cause(err) == ldaputils.ErrUserNotFound {
return nil, httperrors.ErrUserNotFound
}
if errors.Cause(err) == ldaputils.ErrUserBadCredential {
return nil, httperrors.ErrWrongPassword
}
return nil, errors.Wrap(err, "Authenticate error")
}
+4 -2
View File
@@ -17,7 +17,6 @@ package models
import (
"context"
"database/sql"
"fmt"
"time"
"yunion.io/x/jsonutils"
@@ -282,6 +281,9 @@ func (manager *SUserManager) FetchUserExtended(userId, userName, domainId, domai
extUser := api.SUserExtended{}
err := q.First(&extUser)
if err != nil {
if err == sql.ErrNoRows {
return nil, httperrors.ErrUserNotFound
}
return nil, errors.Wrap(err, "query")
}
@@ -312,7 +314,7 @@ func localUserVerifyPassword(user *api.SUserExtended, passwd string) error {
if err == nil {
return nil
}
return errors.Error(fmt.Sprintf("invalid password: %v", err))
return httperrors.ErrWrongPassword
}
// 用户列表
+12 -12
View File
@@ -83,7 +83,7 @@ func authUserByIdentity(ctx context.Context, ident mcclient.SAuthenticationIdent
return nil, sqlchemy.ErrDuplicateEntry
} else if usrCnt == 0 {
log.Errorf("find no user with name %s", ident.Password.User.Name)
return nil, sqlchemy.ErrEmptyQuery
return nil, httperrors.ErrUserNotFound
} else {
// userCnt == 1
usr := models.SUser{}
@@ -99,20 +99,11 @@ func authUserByIdentity(ctx context.Context, ident mcclient.SAuthenticationIdent
usrExt, err := models.UserManager.FetchUserExtended(ident.Password.User.Id, ident.Password.User.Name,
ident.Password.User.Domain.Id, ident.Password.User.Domain.Name)
if err != nil && err != sql.ErrNoRows {
if err != nil && errors.Cause(err) != httperrors.ErrUserNotFound {
return nil, errors.Wrap(err, "UserManager.FetchUserExtended")
}
// checn enable
if !usrExt.Enabled {
if usrExt.IsLocal && usrExt.LocalFailedAuthCount > o.Options.PasswordErrorLockCount {
// user locked
return nil, errors.Wrap(httperrors.ErrUserLocked, "please contact the administrator")
}
// user disabled
return nil, errors.Wrap(httperrors.ErrUserLocked, "please contact the administrator")
}
if err == sql.ErrNoRows {
if err != nil {
// no such user locally, query domain idp
domain, err := models.DomainManager.FetchDomain(ident.Password.User.Domain.Id, ident.Password.User.Domain.Name)
if err != nil {
@@ -124,6 +115,15 @@ func authUserByIdentity(ctx context.Context, ident mcclient.SAuthenticationIdent
}
idpId = mapping.IdpId
} else {
// check enable
if !usrExt.Enabled {
if usrExt.IsLocal && usrExt.LocalFailedAuthCount > o.Options.PasswordErrorLockCount {
// user locked
return nil, httperrors.ErrUserLocked
}
// user disabled
return nil, httperrors.ErrUserLocked
}
// user exists, query user's idp
idps, err := models.IdentityProviderManager.FetchIdentityProvidersByUserId(usrExt.Id, api.PASSWORD_PROTECTED_IDPS)
if err != nil {
+1 -1
View File
@@ -89,7 +89,7 @@ func authenticateTokensV3(ctx context.Context, w http.ResponseWriter, r *http.Re
switch errors.Cause(err) {
case sqlchemy.ErrDuplicateEntry:
httperrors.ConflictError(ctx, w, "duplicate username")
case httperrors.ErrTooManyAttempts, httperrors.ErrUserNotFound, httperrors.ErrUserDisabled, httperrors.ErrUserLocked:
case httperrors.ErrTooManyAttempts, httperrors.ErrUserNotFound, httperrors.ErrUserDisabled, httperrors.ErrUserLocked, httperrors.ErrWrongPassword:
httperrors.GeneralServerError(ctx, w, err)
default:
httperrors.UnauthorizedError(ctx, w, "unauthorized %s", err)