mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-10-28 03:23:25 +08:00
fix: user with sso idp fail to login with password (#7552)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
This commit is contained in:
@@ -60,6 +60,13 @@ func getSsoLinkCallbackUrl() string {
|
||||
return options.Options.SsoLinkCallbackUrl
|
||||
}
|
||||
|
||||
func getSsoUserNotFoundCallbackUrl() string {
|
||||
if options.Options.SsoUserNotFoundCallbackUrl == "" {
|
||||
return getSsoAuthCallbackUrl()
|
||||
}
|
||||
return options.Options.SsoUserNotFoundCallbackUrl
|
||||
}
|
||||
|
||||
func (h *AuthHandlers) getIdpSsoRedirectUri(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
params := appctx.AppContextParams(ctx)
|
||||
idpId := params["<idp_id>"]
|
||||
@@ -164,41 +171,38 @@ func (h *AuthHandlers) handleSsoLogin(ctx context.Context, w http.ResponseWriter
|
||||
|
||||
appsrv.DisableClientCache(w)
|
||||
|
||||
var referer string
|
||||
var idpUserId string
|
||||
if len(idpLinkUser) > 0 {
|
||||
// link with existing user
|
||||
err := linkWithExistingUser(ctx, req, idpId, idpLinkUser, body)
|
||||
referer := getSsoLinkCallbackUrl()
|
||||
refererUrl, _ := url.Parse(referer)
|
||||
if refererUrl == nil {
|
||||
refererUrl, _ = url.Parse(idpReferer)
|
||||
}
|
||||
if err != nil {
|
||||
log.Debugf("error: %s", err)
|
||||
}
|
||||
redirUrl := generateRedirectUrl(refererUrl, idpStateQs, err, "", "")
|
||||
// success, do redirect
|
||||
appsrv.SendRedirect(w, redirUrl)
|
||||
err = linkWithExistingUser(ctx, req, idpId, idpLinkUser, body)
|
||||
referer = getSsoLinkCallbackUrl()
|
||||
} else {
|
||||
// ordinary login
|
||||
referer := getSsoAuthCallbackUrl()
|
||||
refererUrl, _ := url.Parse(referer)
|
||||
if refererUrl == nil {
|
||||
refererUrl, _ = url.Parse(idpReferer)
|
||||
}
|
||||
var idpUserId string
|
||||
err = h.doLogin(ctx, w, req, body)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == httperrors.ErrUserNotFound {
|
||||
idpUserId = findExtUserId(err.Error())
|
||||
if len(idpUserId) == 0 {
|
||||
err = httputils.NewJsonClientError(400, string(httperrors.ErrInputParameter), "empty external user id")
|
||||
} else {
|
||||
referer = getSsoUserNotFoundCallbackUrl()
|
||||
}
|
||||
}
|
||||
log.Debugf("error: %s", err)
|
||||
}
|
||||
redirUrl := generateRedirectUrl(refererUrl, idpStateQs, err, idpId, idpUserId)
|
||||
appsrv.SendRedirect(w, redirUrl)
|
||||
if referer == "" {
|
||||
referer = getSsoAuthCallbackUrl()
|
||||
}
|
||||
}
|
||||
refererUrl, _ := url.Parse(referer)
|
||||
if refererUrl == nil {
|
||||
refererUrl, _ = url.Parse(idpReferer)
|
||||
}
|
||||
if err != nil {
|
||||
log.Debugf("error: %s refererUrl: %s", err, refererUrl)
|
||||
}
|
||||
redirUrl := generateRedirectUrl(refererUrl, idpStateQs, err, idpId, idpUserId)
|
||||
appsrv.SendRedirect(w, redirUrl)
|
||||
}
|
||||
|
||||
func generateRedirectUrl(originUrl *url.URL, stateQs jsonutils.JSONObject, err error, idpId, idpUserId string) string {
|
||||
@@ -224,7 +228,7 @@ func generateRedirectUrl(originUrl *url.URL, stateQs jsonutils.JSONObject, err e
|
||||
qs.(*jsonutils.JSONDict).Add(jsonutils.NewString("error"), "result")
|
||||
if len(idpUserId) > 0 {
|
||||
qs.(*jsonutils.JSONDict).Add(jsonutils.NewString(idpId), "idp_id")
|
||||
qs.(*jsonutils.JSONDict).Add(jsonutils.NewString(idpUserId), "idp_user_id")
|
||||
qs.(*jsonutils.JSONDict).Add(jsonutils.NewString(idpUserId), "idp_entity_id")
|
||||
}
|
||||
} else {
|
||||
qs.(*jsonutils.JSONDict).Add(jsonutils.NewString("success"), "result")
|
||||
|
||||
@@ -33,6 +33,8 @@ type GatewayOptions struct {
|
||||
SsoLinkCallbackUrl string `help:"SSO idp link user callback URL"`
|
||||
LoginCallbackParam string `help:"Redirect callback parameter name after successful login"`
|
||||
|
||||
SsoUserNotFoundCallbackUrl string `help:"failure callback URL when SSO idp link user not found"`
|
||||
|
||||
common_options.CommonOptions `"request_worker_count->default":"32"`
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ type SUserExtended struct {
|
||||
LocalName string
|
||||
DomainName string
|
||||
DomainEnabled bool
|
||||
IsLocal bool
|
||||
// IsLocal bool
|
||||
// IdpId string
|
||||
// IdpName string
|
||||
}
|
||||
|
||||
@@ -276,24 +276,14 @@ func (manager *SUserManager) FetchUserExtended(userId, userName, domainId, domai
|
||||
extUser := api.SUserExtended{}
|
||||
err := q.First(&extUser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "query")
|
||||
}
|
||||
|
||||
idMaps, err := IdmappingManager.FetchEntities(extUser.Id, api.IdMappingEntityUser)
|
||||
if len(idMaps) > 0 {
|
||||
extUser.IsLocal = false
|
||||
} else {
|
||||
extUser.IsLocal = true
|
||||
}
|
||||
return &extUser, nil
|
||||
}
|
||||
|
||||
func VerifyPassword(user *api.SUserExtended, passwd string) error {
|
||||
if user.IsLocal {
|
||||
return localUserVerifyPassword(user, passwd)
|
||||
} else {
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
return localUserVerifyPassword(user, passwd)
|
||||
}
|
||||
|
||||
func localUserVerifyPassword(user *api.SUserExtended, passwd string) error {
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
@@ -71,29 +73,17 @@ func authUserByIdentity(ctx context.Context, ident mcclient.SAuthenticationIdent
|
||||
return nil, ErrEmptyAuth
|
||||
}
|
||||
if len(ident.Password.User.Name) > 0 && len(ident.Password.User.Id) == 0 && len(ident.Password.User.Domain.Id) == 0 && len(ident.Password.User.Domain.Name) == 0 {
|
||||
// no use domain specified, try to find use domain
|
||||
users := models.UserManager.Query().SubQuery()
|
||||
idMappings := models.IdmappingManager.Query().SubQuery()
|
||||
q := users.Query()
|
||||
q = q.LeftJoin(idMappings, sqlchemy.Equals(idMappings.Field("public_id"), users.Field("id")))
|
||||
q = q.Filter(sqlchemy.Equals(users.Field("name"), ident.Password.User.Name))
|
||||
q = q.Filter(sqlchemy.OR(
|
||||
sqlchemy.IsNull(idMappings.Field("domain_id")),
|
||||
sqlchemy.In(idMappings.Field("domain_id"), models.IdentityProviderManager.FetchPasswordProtectedIdpIdsQuery()),
|
||||
))
|
||||
// no user domain specified, try to find user domain
|
||||
q := models.UserManager.Query().Equals("name", ident.Password.User.Name).IsTrue("enabled")
|
||||
usrCnt, err := q.CountWithError()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Query user by name")
|
||||
}
|
||||
if usrCnt > 1 {
|
||||
log.Errorf("find %d user with name %s", usrCnt, ident.Password.User.Name)
|
||||
return nil, sqlchemy.ErrDuplicateEntry
|
||||
} else if usrCnt == 0 {
|
||||
/*idp, err := models.IdentityProviderManager.GetAutoCreateUserProvider()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "IdentityProviderManager.GetAutoCreateUserProvider")
|
||||
}
|
||||
idpId = idp.Id
|
||||
*/
|
||||
log.Errorf("find no user with name %s", ident.Password.User.Name)
|
||||
return nil, sqlchemy.ErrEmptyQuery
|
||||
} else {
|
||||
// userCnt == 1
|
||||
@@ -108,11 +98,13 @@ func authUserByIdentity(ctx context.Context, ident mcclient.SAuthenticationIdent
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "IdentityProviderManager.FetchIdentityProvidersByUserId")
|
||||
}
|
||||
log.Debugf("user %s idps: %s", ident.Password.User.Name, jsonutils.Marshal(idps))
|
||||
if len(idps) == 0 {
|
||||
idpId = api.DEFAULT_IDP_ID
|
||||
} else if len(idps) == 1 {
|
||||
idpId = idps[0].Id
|
||||
} else {
|
||||
log.Errorf("find %d password idps for user %s", len(idps), ident.Password.User.Name)
|
||||
return nil, sqlchemy.ErrDuplicateEntry
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,14 +256,9 @@ func (t *SAuthToken) getTokenV3(
|
||||
token.Token.User.Name = user.Name
|
||||
token.Token.User.Domain.Id = user.DomainId
|
||||
token.Token.User.Domain.Name = user.DomainName
|
||||
if user.IsLocal {
|
||||
lastPass, err := models.PasswordManager.FetchLastPassword(user.LocalId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "FetchLastPassword")
|
||||
}
|
||||
if lastPass != nil && !lastPass.ExpiresAt.IsZero() {
|
||||
token.Token.User.PasswordExpiresAt = lastPass.ExpiresAt
|
||||
}
|
||||
lastPass, _ := models.PasswordManager.FetchLastPassword(user.LocalId)
|
||||
if lastPass != nil && !lastPass.ExpiresAt.IsZero() {
|
||||
token.Token.User.PasswordExpiresAt = lastPass.ExpiresAt
|
||||
}
|
||||
token.Token.User.Displayname = user.Displayname
|
||||
token.Token.User.Email = user.Email
|
||||
|
||||
Reference in New Issue
Block a user