mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 01:47:33 +00:00
fix(login): improve auth handlers (#7969)
# Which Problems Are Solved During the implementation of #7486 it was noticed, that projections in the `auth` database schema could be blocked. Investigations suggested, that this is due to the use of [GORM](https://gorm.io/index.html) and it's inability to use an existing (sql) transaction. With the improved / simplified handling (see below) there should also be a minimal improvement in performance, resp. reduced database update statements. # How the Problems Are Solved The handlers in `auth` are exchanged to proper (sql) statements and gorm usage is removed for any writing part. To further improve / simplify the handling of the users, a new `auth.users3` table is created, where only attributes are handled, which are not yet available from the `projections.users`, `projections.login_name` and `projections.user_auth_methods` do not provide. This reduces the events handled in that specific handler by a lot. # Additional Changes None # Additional Context relates to #7486
This commit is contained in:
@@ -13,13 +13,24 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
RefreshTokenKeyTokenID = "id"
|
||||
RefreshTokenKeyUserID = "user_id"
|
||||
RefreshTokenKeyApplicationID = "application_id"
|
||||
RefreshTokenKeyUserAgentID = "user_agent_id"
|
||||
RefreshTokenKeyExpiration = "expiration"
|
||||
RefreshTokenKeyResourceOwner = "resource_owner"
|
||||
RefreshTokenKeyInstanceID = "instance_id"
|
||||
RefreshTokenKeyTokenID = "id"
|
||||
RefreshTokenKeyUserID = "user_id"
|
||||
RefreshTokenKeyApplicationID = "application_id"
|
||||
RefreshTokenKeyUserAgentID = "user_agent_id"
|
||||
RefreshTokenKeyExpiration = "expiration"
|
||||
RefreshTokenKeyResourceOwner = "resource_owner"
|
||||
RefreshTokenKeyInstanceID = "instance_id"
|
||||
RefreshTokenKeyCreationDate = "creation_date"
|
||||
RefreshTokenKeyChangeDate = "change_date"
|
||||
RefreshTokenKeySequence = "sequence"
|
||||
RefreshTokenKeyActor = "actor"
|
||||
RefreshTokenKeyAMR = "amr"
|
||||
RefreshTokenKeyAuthTime = "auth_time"
|
||||
RefreshTokenKeyAudience = "audience"
|
||||
RefreshTokenKeyClientID = "client_id"
|
||||
RefreshTokenKeyIdleExpiration = "idle_expiration"
|
||||
RefreshTokenKeyScopes = "scopes"
|
||||
RefreshTokenKeyToken = "token"
|
||||
)
|
||||
|
||||
type RefreshTokenView struct {
|
||||
@@ -72,6 +83,7 @@ func RefreshTokenViewToModel(token *RefreshTokenView) *usr_model.RefreshTokenVie
|
||||
}
|
||||
|
||||
func (t *RefreshTokenView) AppendEventIfMyRefreshToken(event eventstore.Event) (err error) {
|
||||
// in case anything needs to be change here check if the Reduce function needs the change as well
|
||||
view := new(RefreshTokenView)
|
||||
switch event.Type() {
|
||||
case user_repo.HumanRefreshTokenAddedType:
|
||||
@@ -101,6 +113,7 @@ func (t *RefreshTokenView) AppendEventIfMyRefreshToken(event eventstore.Event) (
|
||||
}
|
||||
|
||||
func (t *RefreshTokenView) AppendEvent(event eventstore.Event) error {
|
||||
// in case anything needs to be change here check if the Reduce function needs the change as well
|
||||
t.ChangeDate = event.CreatedAt()
|
||||
t.Sequence = event.Sequence()
|
||||
switch event.Type() {
|
||||
@@ -123,7 +136,7 @@ func (t *RefreshTokenView) setRootData(event eventstore.Event) {
|
||||
func (t *RefreshTokenView) appendAddedEvent(event eventstore.Event) error {
|
||||
e := new(user_repo.HumanRefreshTokenAddedEvent)
|
||||
if err := event.Unmarshal(e); err != nil {
|
||||
logging.Log("EVEN-Dbb31").WithError(err).Error("could not unmarshal event data")
|
||||
logging.WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-Bbr42", "could not unmarshal event")
|
||||
}
|
||||
t.ID = e.TokenID
|
||||
@@ -144,7 +157,7 @@ func (t *RefreshTokenView) appendAddedEvent(event eventstore.Event) error {
|
||||
func (t *RefreshTokenView) appendRenewedEvent(event eventstore.Event) error {
|
||||
e := new(user_repo.HumanRefreshTokenRenewedEvent)
|
||||
if err := event.Unmarshal(e); err != nil {
|
||||
logging.Log("EVEN-Vbbn2").WithError(err).Error("could not unmarshal event data")
|
||||
logging.WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-Bbrn4", "could not unmarshal event")
|
||||
}
|
||||
t.ID = e.TokenID
|
||||
|
@@ -16,14 +16,23 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
TokenKeyTokenID = "id"
|
||||
TokenKeyUserID = "user_id"
|
||||
TokenKeyRefreshTokenID = "refresh_token_id"
|
||||
TokenKeyApplicationID = "application_id"
|
||||
TokenKeyUserAgentID = "user_agent_id"
|
||||
TokenKeyExpiration = "expiration"
|
||||
TokenKeyResourceOwner = "resource_owner"
|
||||
TokenKeyInstanceID = "instance_id"
|
||||
TokenKeyTokenID = "id"
|
||||
TokenKeyUserID = "user_id"
|
||||
TokenKeyRefreshTokenID = "refresh_token_id"
|
||||
TokenKeyApplicationID = "application_id"
|
||||
TokenKeyUserAgentID = "user_agent_id"
|
||||
TokenKeyExpiration = "expiration"
|
||||
TokenKeyResourceOwner = "resource_owner"
|
||||
TokenKeyInstanceID = "instance_id"
|
||||
TokenKeyCreationDate = "creation_date"
|
||||
TokenKeyChangeDate = "change_date"
|
||||
TokenKeySequence = "sequence"
|
||||
TokenKeyActor = "actor"
|
||||
TokenKeyID = "id"
|
||||
TokenKeyAudience = "audience"
|
||||
TokenKeyPreferredLanguage = "preferred_language"
|
||||
TokenKeyScopes = "scopes"
|
||||
TokenKeyIsPat = "is_pat"
|
||||
)
|
||||
|
||||
type TokenView struct {
|
||||
@@ -100,6 +109,7 @@ func TokenViewToModel(token *TokenView) *usr_model.TokenView {
|
||||
}
|
||||
|
||||
func (t *TokenView) AppendEventIfMyToken(event eventstore.Event) (err error) {
|
||||
// in case anything needs to be change here check if the Reduce function needs the change as well
|
||||
view := new(TokenView)
|
||||
switch event.Type() {
|
||||
case user_repo.UserTokenAddedType,
|
||||
@@ -112,7 +122,7 @@ func (t *TokenView) AppendEventIfMyToken(event eventstore.Event) (err error) {
|
||||
return t.appendRefreshTokenRemoved(event)
|
||||
case user_repo.UserV1SignedOutType,
|
||||
user_repo.HumanSignedOutType:
|
||||
id, err := agentIDFromSession(event)
|
||||
id, err := UserAgentIDFromEvent(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -146,6 +156,7 @@ func (t *TokenView) AppendEventIfMyToken(event eventstore.Event) (err error) {
|
||||
}
|
||||
|
||||
func (t *TokenView) AppendEvent(event eventstore.Event) error {
|
||||
// in case anything needs to be change here check if the Reduce function needs the change as well
|
||||
t.ChangeDate = event.CreatedAt()
|
||||
t.Sequence = event.Sequence()
|
||||
switch event.Type() {
|
||||
@@ -170,49 +181,40 @@ func (t *TokenView) setRootData(event eventstore.Event) {
|
||||
|
||||
func (t *TokenView) setData(event eventstore.Event) error {
|
||||
if err := event.Unmarshal(t); err != nil {
|
||||
logging.Log("EVEN-3Gm9s").WithError(err).Error("could not unmarshal event data")
|
||||
logging.WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(err, "MODEL-5Gms9", "could not unmarshal event")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func agentIDFromSession(event eventstore.Event) (string, error) {
|
||||
session := make(map[string]interface{})
|
||||
if err := event.Unmarshal(&session); err != nil {
|
||||
logging.Log("EVEN-Ghgt3").WithError(err).Error("could not unmarshal event data")
|
||||
return "", zerrors.ThrowInternal(nil, "MODEL-GBf32", "could not unmarshal data")
|
||||
}
|
||||
return session["userAgentID"].(string), nil
|
||||
}
|
||||
|
||||
func (t *TokenView) appendTokenRemoved(event eventstore.Event) error {
|
||||
token, err := eventToMap(event)
|
||||
tokenID, err := tokenIDFromEvent(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if token["tokenId"] == t.ID {
|
||||
if tokenID == t.ID {
|
||||
t.Deactivated = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TokenView) appendRefreshTokenRemoved(event eventstore.Event) error {
|
||||
refreshToken, err := eventToMap(event)
|
||||
tokenID, err := tokenIDFromEvent(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if refreshToken["tokenId"] == t.RefreshTokenID {
|
||||
if tokenID == t.RefreshTokenID {
|
||||
t.Deactivated = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TokenView) appendPATRemoved(event eventstore.Event) error {
|
||||
pat, err := eventToMap(event)
|
||||
tokenID, err := tokenIDFromEvent(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pat["tokenId"] == t.ID && t.IsPAT {
|
||||
if tokenID == t.ID && t.IsPAT {
|
||||
t.Deactivated = true
|
||||
}
|
||||
return nil
|
||||
@@ -235,11 +237,15 @@ func (t *TokenView) GetRelevantEventTypes() []eventstore.EventType {
|
||||
}
|
||||
}
|
||||
|
||||
func eventToMap(event eventstore.Event) (map[string]interface{}, error) {
|
||||
m := make(map[string]interface{})
|
||||
if err := event.Unmarshal(&m); err != nil {
|
||||
logging.Log("EVEN-Dbffe").WithError(err).Error("could not unmarshal event data")
|
||||
return nil, zerrors.ThrowInternal(nil, "MODEL-SDAfw", "could not unmarshal data")
|
||||
}
|
||||
return m, nil
|
||||
type tokenIDPayload struct {
|
||||
ID string `json:"tokenId"`
|
||||
}
|
||||
|
||||
func tokenIDFromEvent(event eventstore.Event) (string, error) {
|
||||
m := new(tokenIDPayload)
|
||||
if err := event.Unmarshal(&m); err != nil {
|
||||
logging.WithError(err).Error("could not unmarshal event data")
|
||||
return "", zerrors.ThrowInternal(nil, "MODEL-SDAfw", "could not unmarshal data")
|
||||
}
|
||||
return m.ID, nil
|
||||
}
|
||||
|
@@ -18,27 +18,27 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
UserKeyUserID = "id"
|
||||
UserKeyUserName = "user_name"
|
||||
UserKeyFirstName = "first_name"
|
||||
UserKeyLastName = "last_name"
|
||||
UserKeyNickName = "nick_name"
|
||||
UserKeyDisplayName = "display_name"
|
||||
UserKeyEmail = "email"
|
||||
UserKeyState = "user_state"
|
||||
UserKeyResourceOwner = "resource_owner"
|
||||
UserKeyLoginNames = "login_names"
|
||||
UserKeyPreferredLoginName = "preferred_login_name"
|
||||
UserKeyType = "user_type"
|
||||
UserKeyInstanceID = "instance_id"
|
||||
UserKeyOwnerRemoved = "owner_removed"
|
||||
)
|
||||
|
||||
type userType string
|
||||
|
||||
const (
|
||||
userTypeHuman = "human"
|
||||
userTypeMachine = "machine"
|
||||
UserKeyUserID = "id"
|
||||
UserKeyUserName = "user_name"
|
||||
UserKeyFirstName = "first_name"
|
||||
UserKeyLastName = "last_name"
|
||||
UserKeyNickName = "nick_name"
|
||||
UserKeyDisplayName = "display_name"
|
||||
UserKeyEmail = "email"
|
||||
UserKeyState = "user_state"
|
||||
UserKeyResourceOwner = "resource_owner"
|
||||
UserKeyLoginNames = "login_names"
|
||||
UserKeyPreferredLoginName = "preferred_login_name"
|
||||
UserKeyType = "user_type"
|
||||
UserKeyInstanceID = "instance_id"
|
||||
UserKeyOwnerRemoved = "owner_removed"
|
||||
UserKeyPasswordSet = "password_set"
|
||||
UserKeyPasswordInitRequired = "password_init_required"
|
||||
UserKeyPasswordChange = "password_change"
|
||||
UserKeyInitRequired = "init_required"
|
||||
UserKeyPasswordlessInitRequired = "passwordless_init_required"
|
||||
UserKeyMFAInitSkipped = "mfa_init_skipped"
|
||||
UserKeyChangeDate = "change_date"
|
||||
)
|
||||
|
||||
type UserView struct {
|
||||
@@ -51,7 +51,6 @@ type UserView struct {
|
||||
LoginNames database.TextArray[string] `json:"-" gorm:"column:login_names"`
|
||||
PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"`
|
||||
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
||||
Type userType `json:"-" gorm:"column:user_type"`
|
||||
UserName string `json:"userName" gorm:"column:user_name"`
|
||||
InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"`
|
||||
*MachineView
|
||||
@@ -236,13 +235,13 @@ func (u *UserView) SetLoginNames(userLoginMustBeDomain bool, domains []*org_mode
|
||||
}
|
||||
|
||||
func (u *UserView) AppendEvent(event eventstore.Event) (err error) {
|
||||
// in case anything needs to be change here check if the Reduce function needs the change as well
|
||||
u.ChangeDate = event.CreatedAt()
|
||||
u.Sequence = event.Sequence()
|
||||
switch event.Type() {
|
||||
case user.MachineAddedEventType:
|
||||
u.CreationDate = event.CreatedAt()
|
||||
u.setRootData(event)
|
||||
u.Type = userTypeMachine
|
||||
err = u.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -253,7 +252,6 @@ func (u *UserView) AppendEvent(event eventstore.Event) (err error) {
|
||||
user.HumanAddedType:
|
||||
u.CreationDate = event.CreatedAt()
|
||||
u.setRootData(event)
|
||||
u.Type = userTypeHuman
|
||||
err = u.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -396,7 +394,7 @@ func (u *UserView) setData(event eventstore.Event) error {
|
||||
func (u *UserView) setPasswordData(event eventstore.Event) error {
|
||||
password := new(es_model.Password)
|
||||
if err := event.Unmarshal(password); err != nil {
|
||||
logging.Log("MODEL-sdw4r").WithError(err).Error("could not unmarshal event data")
|
||||
logging.WithError(err).Error("could not unmarshal event data")
|
||||
return zerrors.ThrowInternal(nil, "MODEL-6jhsw", "could not unmarshal data")
|
||||
}
|
||||
u.PasswordSet = password.Secret != nil || password.EncodedHash != ""
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
@@ -14,12 +15,23 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
UserSessionKeyUserAgentID = "user_agent_id"
|
||||
UserSessionKeyUserID = "user_id"
|
||||
UserSessionKeyState = "state"
|
||||
UserSessionKeyResourceOwner = "resource_owner"
|
||||
UserSessionKeyInstanceID = "instance_id"
|
||||
UserSessionKeyOwnerRemoved = "owner_removed"
|
||||
UserSessionKeyUserAgentID = "user_agent_id"
|
||||
UserSessionKeyUserID = "user_id"
|
||||
UserSessionKeyState = "state"
|
||||
UserSessionKeyResourceOwner = "resource_owner"
|
||||
UserSessionKeyInstanceID = "instance_id"
|
||||
UserSessionKeyOwnerRemoved = "owner_removed"
|
||||
UserSessionKeyCreationDate = "creation_date"
|
||||
UserSessionKeyChangeDate = "change_date"
|
||||
UserSessionKeySequence = "sequence"
|
||||
UserSessionKeyPasswordVerification = "password_verification"
|
||||
UserSessionKeySecondFactorVerification = "second_factor_verification"
|
||||
UserSessionKeySecondFactorVerificationType = "second_factor_verification_type"
|
||||
UserSessionKeyMultiFactorVerification = "multi_factor_verification"
|
||||
UserSessionKeyMultiFactorVerificationType = "multi_factor_verification_type"
|
||||
UserSessionKeyPasswordlessVerification = "passwordless_verification"
|
||||
UserSessionKeyExternalLoginVerification = "external_login_verification"
|
||||
UserSessionKeySelectedIDPConfigID = "selected_idp_config_id"
|
||||
)
|
||||
|
||||
type UserSessionView struct {
|
||||
@@ -33,29 +45,33 @@ type UserSessionView struct {
|
||||
// are not projected in the user session handler anymore
|
||||
// and are therefore annotated with a `gorm:"-"`.
|
||||
// They will be read from the corresponding projection directly.
|
||||
UserName string `json:"-" gorm:"-"`
|
||||
LoginName string `json:"-" gorm:"-"`
|
||||
DisplayName string `json:"-" gorm:"-"`
|
||||
AvatarKey string `json:"-" gorm:"-"`
|
||||
SelectedIDPConfigID string `json:"selectedIDPConfigID" gorm:"column:selected_idp_config_id"`
|
||||
PasswordVerification time.Time `json:"-" gorm:"column:password_verification"`
|
||||
PasswordlessVerification time.Time `json:"-" gorm:"column:passwordless_verification"`
|
||||
ExternalLoginVerification time.Time `json:"-" gorm:"column:external_login_verification"`
|
||||
SecondFactorVerification time.Time `json:"-" gorm:"column:second_factor_verification"`
|
||||
SecondFactorVerificationType int32 `json:"-" gorm:"column:second_factor_verification_type"`
|
||||
MultiFactorVerification time.Time `json:"-" gorm:"column:multi_factor_verification"`
|
||||
MultiFactorVerificationType int32 `json:"-" gorm:"column:multi_factor_verification_type"`
|
||||
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
||||
InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"`
|
||||
UserName sql.NullString `json:"-" gorm:"-"`
|
||||
LoginName sql.NullString `json:"-" gorm:"-"`
|
||||
DisplayName sql.NullString `json:"-" gorm:"-"`
|
||||
AvatarKey sql.NullString `json:"-" gorm:"-"`
|
||||
SelectedIDPConfigID sql.NullString `json:"selectedIDPConfigID" gorm:"column:selected_idp_config_id"`
|
||||
PasswordVerification sql.NullTime `json:"-" gorm:"column:password_verification"`
|
||||
PasswordlessVerification sql.NullTime `json:"-" gorm:"column:passwordless_verification"`
|
||||
ExternalLoginVerification sql.NullTime `json:"-" gorm:"column:external_login_verification"`
|
||||
SecondFactorVerification sql.NullTime `json:"-" gorm:"column:second_factor_verification"`
|
||||
SecondFactorVerificationType sql.NullInt32 `json:"-" gorm:"column:second_factor_verification_type"`
|
||||
MultiFactorVerification sql.NullTime `json:"-" gorm:"column:multi_factor_verification"`
|
||||
MultiFactorVerificationType sql.NullInt32 `json:"-" gorm:"column:multi_factor_verification_type"`
|
||||
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
||||
InstanceID string `json:"instanceID" gorm:"column:instance_id;primary_key"`
|
||||
}
|
||||
|
||||
func UserSessionFromEvent(event eventstore.Event) (*UserSessionView, error) {
|
||||
v := new(UserSessionView)
|
||||
if err := event.Unmarshal(v); err != nil {
|
||||
logging.Log("EVEN-lso9e").WithError(err).Error("could not unmarshal event data")
|
||||
return nil, zerrors.ThrowInternal(nil, "MODEL-sd325", "could not unmarshal data")
|
||||
type userAgentIDPayload struct {
|
||||
ID string `json:"userAgentID"`
|
||||
}
|
||||
|
||||
func UserAgentIDFromEvent(event eventstore.Event) (string, error) {
|
||||
payload := new(userAgentIDPayload)
|
||||
if err := event.Unmarshal(payload); err != nil {
|
||||
logging.WithError(err).Error("could not unmarshal event data")
|
||||
return "", zerrors.ThrowInternal(nil, "MODEL-HJwk9", "could not unmarshal data")
|
||||
}
|
||||
return v, nil
|
||||
return payload.ID, nil
|
||||
}
|
||||
|
||||
func UserSessionToModel(userSession *UserSessionView) *model.UserSessionView {
|
||||
@@ -66,18 +82,18 @@ func UserSessionToModel(userSession *UserSessionView) *model.UserSessionView {
|
||||
State: domain.UserSessionState(userSession.State),
|
||||
UserAgentID: userSession.UserAgentID,
|
||||
UserID: userSession.UserID,
|
||||
UserName: userSession.UserName,
|
||||
LoginName: userSession.LoginName,
|
||||
DisplayName: userSession.DisplayName,
|
||||
AvatarKey: userSession.AvatarKey,
|
||||
SelectedIDPConfigID: userSession.SelectedIDPConfigID,
|
||||
PasswordVerification: userSession.PasswordVerification,
|
||||
PasswordlessVerification: userSession.PasswordlessVerification,
|
||||
ExternalLoginVerification: userSession.ExternalLoginVerification,
|
||||
SecondFactorVerification: userSession.SecondFactorVerification,
|
||||
SecondFactorVerificationType: domain.MFAType(userSession.SecondFactorVerificationType),
|
||||
MultiFactorVerification: userSession.MultiFactorVerification,
|
||||
MultiFactorVerificationType: domain.MFAType(userSession.MultiFactorVerificationType),
|
||||
UserName: userSession.UserName.String,
|
||||
LoginName: userSession.LoginName.String,
|
||||
DisplayName: userSession.DisplayName.String,
|
||||
AvatarKey: userSession.AvatarKey.String,
|
||||
SelectedIDPConfigID: userSession.SelectedIDPConfigID.String,
|
||||
PasswordVerification: userSession.PasswordVerification.Time,
|
||||
PasswordlessVerification: userSession.PasswordlessVerification.Time,
|
||||
ExternalLoginVerification: userSession.ExternalLoginVerification.Time,
|
||||
SecondFactorVerification: userSession.SecondFactorVerification.Time,
|
||||
SecondFactorVerificationType: domain.MFAType(userSession.SecondFactorVerificationType.Int32),
|
||||
MultiFactorVerification: userSession.MultiFactorVerification.Time,
|
||||
MultiFactorVerificationType: domain.MFAType(userSession.MultiFactorVerificationType.Int32),
|
||||
Sequence: userSession.Sequence,
|
||||
}
|
||||
}
|
||||
@@ -91,12 +107,13 @@ func UserSessionsToModel(userSessions []*UserSessionView) []*model.UserSessionVi
|
||||
}
|
||||
|
||||
func (v *UserSessionView) AppendEvent(event eventstore.Event) error {
|
||||
// in case anything needs to be change here check if the Reduce function needs the change as well
|
||||
v.Sequence = event.Sequence()
|
||||
v.ChangeDate = event.CreatedAt()
|
||||
switch event.Type() {
|
||||
case user.UserV1PasswordCheckSucceededType,
|
||||
user.HumanPasswordCheckSucceededType:
|
||||
v.PasswordVerification = event.CreatedAt()
|
||||
v.PasswordVerification = sql.NullTime{Time: event.CreatedAt(), Valid: true}
|
||||
v.State = int32(domain.UserSessionStateActive)
|
||||
case user.UserIDPLoginCheckSucceededType:
|
||||
data := new(es_model.AuthRequest)
|
||||
@@ -104,21 +121,21 @@ func (v *UserSessionView) AppendEvent(event eventstore.Event) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.ExternalLoginVerification = event.CreatedAt()
|
||||
v.SelectedIDPConfigID = data.SelectedIDPConfigID
|
||||
v.ExternalLoginVerification = sql.NullTime{Time: event.CreatedAt(), Valid: true}
|
||||
v.SelectedIDPConfigID = sql.NullString{String: data.SelectedIDPConfigID, Valid: true}
|
||||
v.State = int32(domain.UserSessionStateActive)
|
||||
case user.HumanPasswordlessTokenCheckSucceededType:
|
||||
v.PasswordlessVerification = event.CreatedAt()
|
||||
v.MultiFactorVerification = event.CreatedAt()
|
||||
v.MultiFactorVerificationType = int32(domain.MFATypeU2FUserVerification)
|
||||
v.PasswordlessVerification = sql.NullTime{Time: event.CreatedAt(), Valid: true}
|
||||
v.MultiFactorVerification = sql.NullTime{Time: event.CreatedAt(), Valid: true}
|
||||
v.MultiFactorVerificationType = sql.NullInt32{Int32: int32(domain.MFATypeU2FUserVerification)}
|
||||
v.State = int32(domain.UserSessionStateActive)
|
||||
case user.HumanPasswordlessTokenCheckFailedType,
|
||||
user.HumanPasswordlessTokenRemovedType:
|
||||
v.PasswordlessVerification = time.Time{}
|
||||
v.MultiFactorVerification = time.Time{}
|
||||
v.PasswordlessVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
||||
v.MultiFactorVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
||||
case user.UserV1PasswordCheckFailedType,
|
||||
user.HumanPasswordCheckFailedType:
|
||||
v.PasswordVerification = time.Time{}
|
||||
v.PasswordVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
||||
case user.UserV1PasswordChangedType,
|
||||
user.HumanPasswordChangedType:
|
||||
data := new(es_model.PasswordChange)
|
||||
@@ -127,7 +144,7 @@ func (v *UserSessionView) AppendEvent(event eventstore.Event) error {
|
||||
return err
|
||||
}
|
||||
if v.UserAgentID != data.UserAgentID {
|
||||
v.PasswordVerification = time.Time{}
|
||||
v.PasswordVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
||||
}
|
||||
case user.HumanMFAOTPVerifiedType:
|
||||
data := new(es_model.OTPVerified)
|
||||
@@ -167,7 +184,7 @@ func (v *UserSessionView) AppendEvent(event eventstore.Event) error {
|
||||
user.HumanU2FTokenRemovedType,
|
||||
user.HumanOTPSMSCheckFailedType,
|
||||
user.HumanOTPEmailCheckFailedType:
|
||||
v.SecondFactorVerification = time.Time{}
|
||||
v.SecondFactorVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
||||
case user.HumanU2FTokenVerifiedType:
|
||||
data := new(es_model.WebAuthNVerify)
|
||||
err := data.SetData(event)
|
||||
@@ -183,24 +200,24 @@ func (v *UserSessionView) AppendEvent(event eventstore.Event) error {
|
||||
user.HumanSignedOutType,
|
||||
user.UserLockedType,
|
||||
user.UserDeactivatedType:
|
||||
v.PasswordlessVerification = time.Time{}
|
||||
v.PasswordVerification = time.Time{}
|
||||
v.SecondFactorVerification = time.Time{}
|
||||
v.SecondFactorVerificationType = int32(domain.MFALevelNotSetUp)
|
||||
v.MultiFactorVerification = time.Time{}
|
||||
v.MultiFactorVerificationType = int32(domain.MFALevelNotSetUp)
|
||||
v.ExternalLoginVerification = time.Time{}
|
||||
v.PasswordlessVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
||||
v.PasswordVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
||||
v.SecondFactorVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
||||
v.SecondFactorVerificationType = sql.NullInt32{Int32: int32(domain.MFALevelNotSetUp)}
|
||||
v.MultiFactorVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
||||
v.MultiFactorVerificationType = sql.NullInt32{Int32: int32(domain.MFALevelNotSetUp)}
|
||||
v.ExternalLoginVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
||||
v.State = int32(domain.UserSessionStateTerminated)
|
||||
case user.UserIDPLinkRemovedType, user.UserIDPLinkCascadeRemovedType:
|
||||
v.ExternalLoginVerification = time.Time{}
|
||||
v.SelectedIDPConfigID = ""
|
||||
v.ExternalLoginVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
||||
v.SelectedIDPConfigID = sql.NullString{String: "", Valid: true}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *UserSessionView) setSecondFactorVerification(verificationTime time.Time, mfaType domain.MFAType) {
|
||||
v.SecondFactorVerification = verificationTime
|
||||
v.SecondFactorVerificationType = int32(mfaType)
|
||||
v.SecondFactorVerification = sql.NullTime{Time: verificationTime, Valid: true}
|
||||
v.SecondFactorVerificationType = sql.NullInt32{Int32: int32(mfaType)}
|
||||
v.State = int32(domain.UserSessionStateActive)
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -33,7 +34,7 @@ func TestAppendEvent(t *testing.T) {
|
||||
event: &es_models.Event{CreationDate: now(), Typ: user.UserV1PasswordCheckSucceededType},
|
||||
userView: &UserSessionView{},
|
||||
},
|
||||
result: &UserSessionView{ChangeDate: now(), PasswordVerification: now()},
|
||||
result: &UserSessionView{ChangeDate: now(), PasswordVerification: sql.NullTime{Time: now(), Valid: true}},
|
||||
},
|
||||
{
|
||||
name: "append human password check succeeded event",
|
||||
@@ -41,23 +42,23 @@ func TestAppendEvent(t *testing.T) {
|
||||
event: &es_models.Event{CreationDate: now(), Typ: user.HumanPasswordCheckSucceededType},
|
||||
userView: &UserSessionView{},
|
||||
},
|
||||
result: &UserSessionView{ChangeDate: now(), PasswordVerification: now()},
|
||||
result: &UserSessionView{ChangeDate: now(), PasswordVerification: sql.NullTime{Time: now(), Valid: true}},
|
||||
},
|
||||
{
|
||||
name: "append user password check failed event",
|
||||
args: args{
|
||||
event: &es_models.Event{CreationDate: now(), Typ: user.UserV1PasswordCheckFailedType},
|
||||
userView: &UserSessionView{PasswordVerification: now()},
|
||||
userView: &UserSessionView{PasswordVerification: sql.NullTime{Time: now(), Valid: true}},
|
||||
},
|
||||
result: &UserSessionView{ChangeDate: now(), PasswordVerification: time.Time{}},
|
||||
result: &UserSessionView{ChangeDate: now(), PasswordVerification: sql.NullTime{Time: time.Time{}, Valid: true}},
|
||||
},
|
||||
{
|
||||
name: "append human password check failed event",
|
||||
args: args{
|
||||
event: &es_models.Event{CreationDate: now(), Typ: user.HumanPasswordCheckFailedType},
|
||||
userView: &UserSessionView{PasswordVerification: now()},
|
||||
userView: &UserSessionView{PasswordVerification: sql.NullTime{Time: now(), Valid: true}},
|
||||
},
|
||||
result: &UserSessionView{ChangeDate: now(), PasswordVerification: time.Time{}},
|
||||
result: &UserSessionView{ChangeDate: now(), PasswordVerification: sql.NullTime{Time: time.Time{}, Valid: true}},
|
||||
},
|
||||
{
|
||||
name: "append user password changed event",
|
||||
@@ -72,9 +73,9 @@ func TestAppendEvent(t *testing.T) {
|
||||
return d
|
||||
}(),
|
||||
},
|
||||
userView: &UserSessionView{UserAgentID: "id", PasswordVerification: now()},
|
||||
userView: &UserSessionView{UserAgentID: "id", PasswordVerification: sql.NullTime{Time: now(), Valid: true}},
|
||||
},
|
||||
result: &UserSessionView{UserAgentID: "id", ChangeDate: now(), PasswordVerification: time.Time{}},
|
||||
result: &UserSessionView{UserAgentID: "id", ChangeDate: now(), PasswordVerification: sql.NullTime{Time: time.Time{}, Valid: true}},
|
||||
},
|
||||
{
|
||||
name: "append human password changed event",
|
||||
@@ -91,9 +92,9 @@ func TestAppendEvent(t *testing.T) {
|
||||
return d
|
||||
}(),
|
||||
},
|
||||
userView: &UserSessionView{UserAgentID: "id", PasswordVerification: now()},
|
||||
userView: &UserSessionView{UserAgentID: "id", PasswordVerification: sql.NullTime{Time: now(), Valid: true}},
|
||||
},
|
||||
result: &UserSessionView{UserAgentID: "id", ChangeDate: now(), PasswordVerification: time.Time{}},
|
||||
result: &UserSessionView{UserAgentID: "id", ChangeDate: now(), PasswordVerification: sql.NullTime{Time: time.Time{}, Valid: true}},
|
||||
},
|
||||
{
|
||||
name: "append human password changed event same user agent",
|
||||
@@ -111,9 +112,9 @@ func TestAppendEvent(t *testing.T) {
|
||||
return d
|
||||
}(),
|
||||
},
|
||||
userView: &UserSessionView{UserAgentID: "id", PasswordVerification: now()},
|
||||
userView: &UserSessionView{UserAgentID: "id", PasswordVerification: sql.NullTime{Time: now(), Valid: true}},
|
||||
},
|
||||
result: &UserSessionView{UserAgentID: "id", ChangeDate: now(), PasswordVerification: now()},
|
||||
result: &UserSessionView{UserAgentID: "id", ChangeDate: now(), PasswordVerification: sql.NullTime{Time: now(), Valid: true}},
|
||||
},
|
||||
{
|
||||
name: "append user otp verified event",
|
||||
@@ -142,7 +143,7 @@ func TestAppendEvent(t *testing.T) {
|
||||
},
|
||||
userView: &UserSessionView{UserAgentID: "id"},
|
||||
},
|
||||
result: &UserSessionView{UserAgentID: "id", ChangeDate: now(), SecondFactorVerification: now()},
|
||||
result: &UserSessionView{UserAgentID: "id", ChangeDate: now(), SecondFactorVerification: sql.NullTime{Time: now(), Valid: true}},
|
||||
},
|
||||
{
|
||||
name: "append user otp check succeeded event",
|
||||
@@ -150,7 +151,7 @@ func TestAppendEvent(t *testing.T) {
|
||||
event: &es_models.Event{CreationDate: now(), Typ: user.UserV1MFAOTPCheckSucceededType},
|
||||
userView: &UserSessionView{},
|
||||
},
|
||||
result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: now()},
|
||||
result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: sql.NullTime{Time: now(), Valid: true}},
|
||||
},
|
||||
{
|
||||
name: "append human otp check succeeded event",
|
||||
@@ -158,55 +159,77 @@ func TestAppendEvent(t *testing.T) {
|
||||
event: &es_models.Event{CreationDate: now(), Typ: user.HumanMFAOTPCheckSucceededType},
|
||||
userView: &UserSessionView{},
|
||||
},
|
||||
result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: now()},
|
||||
result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: sql.NullTime{Time: now(), Valid: true}},
|
||||
},
|
||||
{
|
||||
name: "append user otp check failed event",
|
||||
args: args{
|
||||
event: &es_models.Event{CreationDate: now(), Typ: user.UserV1MFAOTPCheckFailedType},
|
||||
userView: &UserSessionView{SecondFactorVerification: now()},
|
||||
userView: &UserSessionView{SecondFactorVerification: sql.NullTime{Time: now(), Valid: true}},
|
||||
},
|
||||
result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: time.Time{}},
|
||||
result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: sql.NullTime{Time: time.Time{}, Valid: true}},
|
||||
},
|
||||
{
|
||||
name: "append human otp check failed event",
|
||||
args: args{
|
||||
event: &es_models.Event{CreationDate: now(), Typ: user.HumanMFAOTPCheckFailedType},
|
||||
userView: &UserSessionView{SecondFactorVerification: now()},
|
||||
userView: &UserSessionView{SecondFactorVerification: sql.NullTime{Time: now(), Valid: true}},
|
||||
},
|
||||
result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: time.Time{}},
|
||||
result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: sql.NullTime{Time: time.Time{}, Valid: true}},
|
||||
},
|
||||
{
|
||||
name: "append user otp removed event",
|
||||
args: args{
|
||||
event: &es_models.Event{CreationDate: now(), Typ: user.UserV1MFAOTPRemovedType},
|
||||
userView: &UserSessionView{SecondFactorVerification: now()},
|
||||
userView: &UserSessionView{SecondFactorVerification: sql.NullTime{Time: now(), Valid: true}},
|
||||
},
|
||||
result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: time.Time{}},
|
||||
result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: sql.NullTime{Time: time.Time{}, Valid: true}},
|
||||
},
|
||||
{
|
||||
name: "append human otp removed event",
|
||||
args: args{
|
||||
event: &es_models.Event{CreationDate: now(), Typ: user.HumanMFAOTPRemovedType},
|
||||
userView: &UserSessionView{SecondFactorVerification: now()},
|
||||
userView: &UserSessionView{SecondFactorVerification: sql.NullTime{Time: now(), Valid: true}},
|
||||
},
|
||||
result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: time.Time{}},
|
||||
result: &UserSessionView{ChangeDate: now(), SecondFactorVerification: sql.NullTime{Time: time.Time{}, Valid: true}},
|
||||
},
|
||||
{
|
||||
name: "append user signed out event",
|
||||
args: args{
|
||||
event: &es_models.Event{CreationDate: now(), Typ: user.UserV1SignedOutType},
|
||||
userView: &UserSessionView{PasswordVerification: now(), SecondFactorVerification: now()},
|
||||
event: &es_models.Event{CreationDate: now(), Typ: user.UserV1SignedOutType},
|
||||
userView: &UserSessionView{
|
||||
PasswordVerification: sql.NullTime{Time: now(), Valid: true},
|
||||
SecondFactorVerification: sql.NullTime{Time: now(), Valid: true},
|
||||
},
|
||||
},
|
||||
result: &UserSessionView{
|
||||
ChangeDate: now(),
|
||||
PasswordVerification: sql.NullTime{Time: time.Time{}, Valid: true},
|
||||
SecondFactorVerification: sql.NullTime{Time: time.Time{}, Valid: true},
|
||||
ExternalLoginVerification: sql.NullTime{Time: time.Time{}, Valid: true},
|
||||
PasswordlessVerification: sql.NullTime{Time: time.Time{}, Valid: true},
|
||||
MultiFactorVerification: sql.NullTime{Time: time.Time{}, Valid: true},
|
||||
State: 1,
|
||||
},
|
||||
result: &UserSessionView{ChangeDate: now(), PasswordVerification: time.Time{}, SecondFactorVerification: time.Time{}, State: 1},
|
||||
},
|
||||
{
|
||||
name: "append human signed out event",
|
||||
args: args{
|
||||
event: &es_models.Event{CreationDate: now(), Typ: user.HumanSignedOutType},
|
||||
userView: &UserSessionView{PasswordVerification: now(), SecondFactorVerification: now()},
|
||||
event: &es_models.Event{CreationDate: now(), Typ: user.HumanSignedOutType},
|
||||
userView: &UserSessionView{
|
||||
PasswordVerification: sql.NullTime{Time: now(), Valid: true},
|
||||
SecondFactorVerification: sql.NullTime{Time: now(), Valid: true},
|
||||
},
|
||||
},
|
||||
result: &UserSessionView{
|
||||
ChangeDate: now(),
|
||||
PasswordVerification: sql.NullTime{Time: time.Time{}, Valid: true},
|
||||
SecondFactorVerification: sql.NullTime{Time: time.Time{}, Valid: true},
|
||||
ExternalLoginVerification: sql.NullTime{Time: time.Time{}, Valid: true},
|
||||
PasswordlessVerification: sql.NullTime{Time: time.Time{}, Valid: true},
|
||||
MultiFactorVerification: sql.NullTime{Time: time.Time{}, Valid: true},
|
||||
State: 1,
|
||||
},
|
||||
result: &UserSessionView{ChangeDate: now(), PasswordVerification: time.Time{}, SecondFactorVerification: time.Time{}, State: 1},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
Reference in New Issue
Block a user