diff --git a/pkg/apigateway/handler/auth.go b/pkg/apigateway/handler/auth.go index 0f230e0b6f..d4e0b612c9 100644 --- a/pkg/apigateway/handler/auth.go +++ b/pkg/apigateway/handler/auth.go @@ -850,7 +850,7 @@ func getUserInfo2(s *mcclient.ClientSession, uid string, pid string, loginIp str "created_at", "enable_mfa", "is_system_account", "last_active_at", "last_login_ip", "last_login_source", - "password_expires_at", "failed_auth_count", "failed_auth_at", + "password_expires_at", "failed_auth_count", "failed_auth_at", "need_reset_password", "idps", "is_local", } { diff --git a/pkg/apis/identity/user.go b/pkg/apis/identity/user.go index 71c7516597..bff45e69e2 100644 --- a/pkg/apis/identity/user.go +++ b/pkg/apis/identity/user.go @@ -31,6 +31,8 @@ type UserDetails struct { FailedAuthAt time.Time `json:"failed_auth_at"` PasswordExpiresAt time.Time `json:"password_expires_at"` + NeedResetPassword bool `json:"need_reset_password"` + Idps []IdpResourceInfo `json:"idps"` IsLocal bool `json:"is_local"` diff --git a/pkg/keystone/models/localusers.go b/pkg/keystone/models/localusers.go index 2a28bac946..1ad68eed4e 100644 --- a/pkg/keystone/models/localusers.go +++ b/pkg/keystone/models/localusers.go @@ -21,6 +21,7 @@ import ( "time" "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/tristate" "yunion.io/x/sqlchemy" "yunion.io/x/onecloud/pkg/cloudcommon/db" @@ -67,6 +68,8 @@ type SLocalUser struct { Name string `width:"255" charset:"utf8" nullable:"false"` FailedAuthCount int `nullable:"true"` FailedAuthAt time.Time `nullable:"true"` + + NeedResetPassword tristate.TriState `nullable:"false" default:"false" list:"domain"` } func (user *SLocalUser) GetId() string { @@ -166,3 +169,21 @@ func (usr *SLocalUser) ClearFailedAuth() error { } return nil } + +func (usr *SLocalUser) markNeedResetPassword(needReset bool) error { + if usr.NeedResetPassword.IsTrue() == needReset { + return nil + } + _, err := db.Update(usr, func() error { + if needReset { + usr.NeedResetPassword = tristate.True + } else { + usr.NeedResetPassword = tristate.False + } + return nil + }) + if err != nil { + return errors.Wrap(err, "Update") + } + return nil +} diff --git a/pkg/keystone/models/users.go b/pkg/keystone/models/users.go index a357ed6b21..764f752c45 100644 --- a/pkg/keystone/models/users.go +++ b/pkg/keystone/models/users.go @@ -192,7 +192,7 @@ func (manager *SUserManager) initSysUser(ctx context.Context) error { if err != nil { return errors.Wrap(err, "ResetAdminUserPassword Query user") } - err = usr.initLocalData(o.Options.BootstrapAdminUserPassword) + err = usr.initLocalData(o.Options.BootstrapAdminUserPassword, false) if err != nil { return errors.Wrap(err, "initLocalData") } @@ -218,7 +218,7 @@ func (manager *SUserManager) initSysUser(ctx context.Context) error { if err != nil { return errors.Wrap(err, "insert") } - err = usr.initLocalData(o.Options.BootstrapAdminUserPassword) + err = usr.initLocalData(o.Options.BootstrapAdminUserPassword, false) if err != nil { return errors.Wrap(err, "initLocalData") } @@ -638,6 +638,9 @@ func userExtra(user *SUser, out api.UserDetails) api.UserDetails { out.FailedAuthCount = localUser.FailedAuthCount out.FailedAuthAt = localUser.FailedAuthAt } + if localUser.NeedResetPassword.IsTrue() { + out.NeedResetPassword = true + } localPass, _ := PasswordManager.FetchLastPassword(localUser.Id) if localPass != nil && !localPass.ExpiresAt.IsZero() { out.PasswordExpiresAt = localPass.ExpiresAt @@ -661,7 +664,7 @@ func userExtra(user *SUser, out api.UserDetails) api.UserDetails { return out } -func (user *SUser) initLocalData(passwd string) error { +func (user *SUser) initLocalData(passwd string, skipPassCheck bool) error { localUsr, err := LocalUserManager.register(user.Id, user.DomainId, user.Name) if err != nil { return errors.Wrap(err, "register localuser") @@ -671,6 +674,11 @@ func (user *SUser) initLocalData(passwd string) error { if err != nil { return errors.Wrap(err, "save password") } + if skipPassCheck { + localUsr.markNeedResetPassword(true) + } else { + localUsr.markNeedResetPassword(false) + } } return nil } @@ -680,7 +688,8 @@ func (user *SUser) PostCreate(ctx context.Context, userCred mcclient.TokenCreden // set password passwd, _ := data.GetString("password") - err := user.initLocalData(passwd) + skipPassCheck := jsonutils.QueryBoolean(data, "skip_password_complexity_check", false) + err := user.initLocalData(passwd, skipPassCheck) if err != nil { log.Errorf("fail to register localUser %s", err) return @@ -721,6 +730,17 @@ func (user *SUser) PostUpdate(ctx context.Context, userCred mcclient.TokenCreden log.Errorf("fail to set password %s", err) return } + localUsr, err := LocalUserManager.fetchLocalUser("", "", usrExt.LocalId) + if err != nil { + log.Errorf("Fail to fetch localUser %d: %s", usrExt.LocalId, err) + } else { + skipPassCheck := jsonutils.QueryBoolean(data, "skip_password_complexity_check", false) + if skipPassCheck { + localUsr.markNeedResetPassword(true) + } else { + localUsr.markNeedResetPassword(false) + } + } logclient.AddActionLogWithContext(ctx, user, logclient.ACT_UPDATE_PASSWORD, nil, userCred, true) } if enabled, _ := data.Bool("enabled"); enabled {