2020-05-18 10:06:36 +00:00
|
|
|
package model
|
|
|
|
|
|
|
|
import (
|
2024-05-22 15:26:02 +00:00
|
|
|
"database/sql"
|
2020-05-18 10:06:36 +00:00
|
|
|
"time"
|
|
|
|
|
2022-04-26 23:01:45 +00:00
|
|
|
"github.com/zitadel/logging"
|
2020-05-18 10:06:36 +00:00
|
|
|
|
2022-04-26 23:01:45 +00:00
|
|
|
"github.com/zitadel/zitadel/internal/domain"
|
|
|
|
"github.com/zitadel/zitadel/internal/eventstore"
|
|
|
|
"github.com/zitadel/zitadel/internal/repository/user"
|
|
|
|
"github.com/zitadel/zitadel/internal/user/model"
|
|
|
|
es_model "github.com/zitadel/zitadel/internal/user/repository/eventsourcing/model"
|
2023-12-08 14:30:55 +00:00
|
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
2020-05-18 10:06:36 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2024-05-22 15:26:02 +00:00
|
|
|
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"
|
2020-05-18 10:06:36 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type UserSessionView struct {
|
2024-05-28 08:59:49 +00:00
|
|
|
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
|
|
|
|
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
|
|
|
|
ResourceOwner string `json:"-" gorm:"column:resource_owner"`
|
|
|
|
State sql.Null[domain.UserSessionState] `json:"-" gorm:"column:state"`
|
|
|
|
UserAgentID string `json:"userAgentID" gorm:"column:user_agent_id;primary_key"`
|
|
|
|
UserID string `json:"userID" gorm:"column:user_id;primary_key"`
|
2024-01-30 15:17:54 +00:00
|
|
|
// As of https://github.com/zitadel/zitadel/pull/7199 the following 4 attributes
|
|
|
|
// 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.
|
2024-05-22 15:26:02 +00:00
|
|
|
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"`
|
2020-05-18 10:06:36 +00:00
|
|
|
}
|
|
|
|
|
2024-05-22 15:26:02 +00:00
|
|
|
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")
|
2020-05-18 10:06:36 +00:00
|
|
|
}
|
2024-05-22 15:26:02 +00:00
|
|
|
return payload.ID, nil
|
2020-05-18 10:06:36 +00:00
|
|
|
}
|
|
|
|
|
2022-05-30 11:27:52 +00:00
|
|
|
func UserSessionToModel(userSession *UserSessionView) *model.UserSessionView {
|
2020-05-18 10:06:36 +00:00
|
|
|
return &model.UserSessionView{
|
2020-11-04 10:26:10 +00:00
|
|
|
ChangeDate: userSession.ChangeDate,
|
|
|
|
CreationDate: userSession.CreationDate,
|
|
|
|
ResourceOwner: userSession.ResourceOwner,
|
2024-05-28 08:59:49 +00:00
|
|
|
State: userSession.State.V,
|
2020-11-04 10:26:10 +00:00
|
|
|
UserAgentID: userSession.UserAgentID,
|
|
|
|
UserID: userSession.UserID,
|
2024-05-22 15:26:02 +00:00
|
|
|
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),
|
2020-11-04 10:26:10 +00:00
|
|
|
Sequence: userSession.Sequence,
|
2020-05-18 10:06:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-30 11:27:52 +00:00
|
|
|
func UserSessionsToModel(userSessions []*UserSessionView) []*model.UserSessionView {
|
2020-05-18 10:06:36 +00:00
|
|
|
result := make([]*model.UserSessionView, len(userSessions))
|
|
|
|
for i, s := range userSessions {
|
2022-05-30 11:27:52 +00:00
|
|
|
result[i] = UserSessionToModel(s)
|
2020-05-18 10:06:36 +00:00
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2023-10-19 10:19:10 +00:00
|
|
|
func (v *UserSessionView) AppendEvent(event eventstore.Event) error {
|
2024-05-22 15:26:02 +00:00
|
|
|
// in case anything needs to be change here check if the Reduce function needs the change as well
|
2023-10-19 10:19:10 +00:00
|
|
|
v.Sequence = event.Sequence()
|
|
|
|
v.ChangeDate = event.CreatedAt()
|
|
|
|
switch event.Type() {
|
2022-03-31 09:36:26 +00:00
|
|
|
case user.UserV1PasswordCheckSucceededType,
|
|
|
|
user.HumanPasswordCheckSucceededType:
|
2024-05-22 15:26:02 +00:00
|
|
|
v.PasswordVerification = sql.NullTime{Time: event.CreatedAt(), Valid: true}
|
2024-05-28 08:59:49 +00:00
|
|
|
v.State.V = domain.UserSessionStateActive
|
2022-03-31 09:36:26 +00:00
|
|
|
case user.UserIDPLoginCheckSucceededType:
|
2020-10-02 06:02:09 +00:00
|
|
|
data := new(es_model.AuthRequest)
|
2020-12-07 11:09:10 +00:00
|
|
|
err := data.SetData(event)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-05-22 15:26:02 +00:00
|
|
|
v.ExternalLoginVerification = sql.NullTime{Time: event.CreatedAt(), Valid: true}
|
|
|
|
v.SelectedIDPConfigID = sql.NullString{String: data.SelectedIDPConfigID, Valid: true}
|
2024-05-28 08:59:49 +00:00
|
|
|
v.State.V = domain.UserSessionStateActive
|
2022-03-31 09:36:26 +00:00
|
|
|
case user.HumanPasswordlessTokenCheckSucceededType:
|
2024-05-22 15:26:02 +00:00
|
|
|
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)}
|
2024-05-28 08:59:49 +00:00
|
|
|
v.State.V = domain.UserSessionStateActive
|
2022-03-31 09:36:26 +00:00
|
|
|
case user.HumanPasswordlessTokenCheckFailedType,
|
|
|
|
user.HumanPasswordlessTokenRemovedType:
|
2024-05-22 15:26:02 +00:00
|
|
|
v.PasswordlessVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
|
|
|
v.MultiFactorVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
2022-03-31 09:36:26 +00:00
|
|
|
case user.UserV1PasswordCheckFailedType,
|
|
|
|
user.HumanPasswordCheckFailedType:
|
2024-05-22 15:26:02 +00:00
|
|
|
v.PasswordVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
2022-03-31 09:36:26 +00:00
|
|
|
case user.UserV1PasswordChangedType,
|
|
|
|
user.HumanPasswordChangedType:
|
2020-12-07 11:09:10 +00:00
|
|
|
data := new(es_model.PasswordChange)
|
|
|
|
err := data.SetData(event)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if v.UserAgentID != data.UserAgentID {
|
2024-05-22 15:26:02 +00:00
|
|
|
v.PasswordVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
2020-12-07 11:09:10 +00:00
|
|
|
}
|
2022-03-31 09:36:26 +00:00
|
|
|
case user.HumanMFAOTPVerifiedType:
|
2020-12-07 11:09:10 +00:00
|
|
|
data := new(es_model.OTPVerified)
|
|
|
|
err := data.SetData(event)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if v.UserAgentID == data.UserAgentID {
|
2023-10-19 10:19:10 +00:00
|
|
|
v.setSecondFactorVerification(event.CreatedAt(), domain.MFATypeTOTP)
|
2020-12-07 11:09:10 +00:00
|
|
|
}
|
2022-03-31 09:36:26 +00:00
|
|
|
case user.UserV1MFAOTPCheckSucceededType,
|
|
|
|
user.HumanMFAOTPCheckSucceededType:
|
2023-10-19 10:19:10 +00:00
|
|
|
v.setSecondFactorVerification(event.CreatedAt(), domain.MFATypeTOTP)
|
2023-08-15 12:47:05 +00:00
|
|
|
case user.HumanOTPSMSCheckSucceededType:
|
|
|
|
data := new(es_model.OTPVerified)
|
|
|
|
err := data.SetData(event)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if v.UserAgentID == data.UserAgentID {
|
2023-10-19 10:19:10 +00:00
|
|
|
v.setSecondFactorVerification(event.CreatedAt(), domain.MFATypeOTPSMS)
|
2023-08-15 12:47:05 +00:00
|
|
|
}
|
|
|
|
case user.HumanOTPEmailCheckSucceededType:
|
|
|
|
data := new(es_model.OTPVerified)
|
|
|
|
err := data.SetData(event)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if v.UserAgentID == data.UserAgentID {
|
2023-10-19 10:19:10 +00:00
|
|
|
v.setSecondFactorVerification(event.CreatedAt(), domain.MFATypeOTPEmail)
|
2023-08-15 12:47:05 +00:00
|
|
|
}
|
2022-03-31 09:36:26 +00:00
|
|
|
case user.UserV1MFAOTPCheckFailedType,
|
|
|
|
user.UserV1MFAOTPRemovedType,
|
|
|
|
user.HumanMFAOTPCheckFailedType,
|
|
|
|
user.HumanMFAOTPRemovedType,
|
|
|
|
user.HumanU2FTokenCheckFailedType,
|
2023-08-15 12:47:05 +00:00
|
|
|
user.HumanU2FTokenRemovedType,
|
|
|
|
user.HumanOTPSMSCheckFailedType,
|
|
|
|
user.HumanOTPEmailCheckFailedType:
|
2024-05-22 15:26:02 +00:00
|
|
|
v.SecondFactorVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
2022-03-31 09:36:26 +00:00
|
|
|
case user.HumanU2FTokenVerifiedType:
|
2020-12-07 11:09:10 +00:00
|
|
|
data := new(es_model.WebAuthNVerify)
|
|
|
|
err := data.SetData(event)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if v.UserAgentID == data.UserAgentID {
|
2023-10-19 10:19:10 +00:00
|
|
|
v.setSecondFactorVerification(event.CreatedAt(), domain.MFATypeU2F)
|
2020-12-07 11:09:10 +00:00
|
|
|
}
|
2022-03-31 09:36:26 +00:00
|
|
|
case user.HumanU2FTokenCheckSucceededType:
|
2023-10-19 10:19:10 +00:00
|
|
|
v.setSecondFactorVerification(event.CreatedAt(), domain.MFATypeU2F)
|
2022-03-31 09:36:26 +00:00
|
|
|
case user.UserV1SignedOutType,
|
|
|
|
user.HumanSignedOutType,
|
|
|
|
user.UserLockedType,
|
|
|
|
user.UserDeactivatedType:
|
2024-05-22 15:26:02 +00:00
|
|
|
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}
|
2024-05-28 08:59:49 +00:00
|
|
|
v.State.V = domain.UserSessionStateTerminated
|
2022-03-31 09:36:26 +00:00
|
|
|
case user.UserIDPLinkRemovedType, user.UserIDPLinkCascadeRemovedType:
|
2024-05-22 15:26:02 +00:00
|
|
|
v.ExternalLoginVerification = sql.NullTime{Time: time.Time{}, Valid: true}
|
|
|
|
v.SelectedIDPConfigID = sql.NullString{String: "", Valid: true}
|
2020-05-18 10:06:36 +00:00
|
|
|
}
|
2020-12-07 11:09:10 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-03-31 09:36:26 +00:00
|
|
|
func (v *UserSessionView) setSecondFactorVerification(verificationTime time.Time, mfaType domain.MFAType) {
|
2024-05-22 15:26:02 +00:00
|
|
|
v.SecondFactorVerification = sql.NullTime{Time: verificationTime, Valid: true}
|
|
|
|
v.SecondFactorVerificationType = sql.NullInt32{Int32: int32(mfaType)}
|
2024-05-28 08:59:49 +00:00
|
|
|
v.State.V = domain.UserSessionStateActive
|
2020-05-18 10:06:36 +00:00
|
|
|
}
|
2022-06-03 12:37:24 +00:00
|
|
|
|
2023-10-19 10:19:10 +00:00
|
|
|
func (v *UserSessionView) EventTypes() []eventstore.EventType {
|
|
|
|
return []eventstore.EventType{
|
|
|
|
user.UserV1PasswordCheckSucceededType,
|
|
|
|
user.HumanPasswordCheckSucceededType,
|
|
|
|
user.UserIDPLoginCheckSucceededType,
|
|
|
|
user.HumanPasswordlessTokenCheckSucceededType,
|
|
|
|
user.HumanPasswordlessTokenCheckFailedType,
|
|
|
|
user.HumanPasswordlessTokenRemovedType,
|
|
|
|
user.UserV1PasswordCheckFailedType,
|
|
|
|
user.HumanPasswordCheckFailedType,
|
|
|
|
user.UserV1PasswordChangedType,
|
|
|
|
user.HumanPasswordChangedType,
|
|
|
|
user.HumanMFAOTPVerifiedType,
|
|
|
|
user.UserV1MFAOTPCheckSucceededType,
|
|
|
|
user.HumanMFAOTPCheckSucceededType,
|
|
|
|
user.UserV1MFAOTPCheckFailedType,
|
|
|
|
user.UserV1MFAOTPRemovedType,
|
|
|
|
user.HumanMFAOTPCheckFailedType,
|
|
|
|
user.HumanMFAOTPRemovedType,
|
|
|
|
user.HumanOTPSMSCheckSucceededType,
|
|
|
|
user.HumanOTPSMSCheckFailedType,
|
|
|
|
user.HumanOTPEmailCheckSucceededType,
|
|
|
|
user.HumanOTPEmailCheckFailedType,
|
|
|
|
user.HumanU2FTokenCheckFailedType,
|
|
|
|
user.HumanU2FTokenRemovedType,
|
|
|
|
user.HumanU2FTokenVerifiedType,
|
|
|
|
user.HumanU2FTokenCheckSucceededType,
|
|
|
|
user.UserV1SignedOutType,
|
|
|
|
user.HumanSignedOutType,
|
|
|
|
user.UserLockedType,
|
|
|
|
user.UserDeactivatedType,
|
|
|
|
user.UserIDPLinkRemovedType,
|
|
|
|
user.UserIDPLinkCascadeRemovedType,
|
2023-07-27 12:10:19 +00:00
|
|
|
}
|
|
|
|
}
|