package projection import ( "context" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/handler" "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/session" "github.com/zitadel/zitadel/internal/repository/user" ) const ( SessionsProjectionTable = "projections.sessions3" SessionColumnID = "id" SessionColumnCreationDate = "creation_date" SessionColumnChangeDate = "change_date" SessionColumnSequence = "sequence" SessionColumnState = "state" SessionColumnResourceOwner = "resource_owner" SessionColumnDomain = "domain" SessionColumnInstanceID = "instance_id" SessionColumnCreator = "creator" SessionColumnUserID = "user_id" SessionColumnUserCheckedAt = "user_checked_at" SessionColumnPasswordCheckedAt = "password_checked_at" SessionColumnIntentCheckedAt = "intent_checked_at" SessionColumnPasskeyCheckedAt = "passkey_checked_at" SessionColumnMetadata = "metadata" SessionColumnTokenID = "token_id" ) type sessionProjection struct { crdb.StatementHandler } func newSessionProjection(ctx context.Context, config crdb.StatementHandlerConfig) *sessionProjection { p := new(sessionProjection) config.ProjectionName = SessionsProjectionTable config.Reducers = p.reducers() config.InitCheck = crdb.NewMultiTableCheck( crdb.NewTable([]*crdb.Column{ crdb.NewColumn(SessionColumnID, crdb.ColumnTypeText), crdb.NewColumn(SessionColumnCreationDate, crdb.ColumnTypeTimestamp), crdb.NewColumn(SessionColumnChangeDate, crdb.ColumnTypeTimestamp), crdb.NewColumn(SessionColumnSequence, crdb.ColumnTypeInt64), crdb.NewColumn(SessionColumnState, crdb.ColumnTypeEnum), crdb.NewColumn(SessionColumnResourceOwner, crdb.ColumnTypeText), crdb.NewColumn(SessionColumnDomain, crdb.ColumnTypeText), crdb.NewColumn(SessionColumnInstanceID, crdb.ColumnTypeText), crdb.NewColumn(SessionColumnCreator, crdb.ColumnTypeText), crdb.NewColumn(SessionColumnUserID, crdb.ColumnTypeText, crdb.Nullable()), crdb.NewColumn(SessionColumnUserCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()), crdb.NewColumn(SessionColumnPasswordCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()), crdb.NewColumn(SessionColumnIntentCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()), crdb.NewColumn(SessionColumnPasskeyCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()), crdb.NewColumn(SessionColumnMetadata, crdb.ColumnTypeJSONB, crdb.Nullable()), crdb.NewColumn(SessionColumnTokenID, crdb.ColumnTypeText, crdb.Nullable()), }, crdb.NewPrimaryKey(SessionColumnInstanceID, SessionColumnID), ), ) p.StatementHandler = crdb.NewStatementHandler(ctx, config) return p } func (p *sessionProjection) reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: session.AggregateType, EventRedusers: []handler.EventReducer{ { Event: session.AddedType, Reduce: p.reduceSessionAdded, }, { Event: session.UserCheckedType, Reduce: p.reduceUserChecked, }, { Event: session.PasswordCheckedType, Reduce: p.reducePasswordChecked, }, { Event: session.IntentCheckedType, Reduce: p.reduceIntentChecked, }, { Event: session.PasskeyCheckedType, Reduce: p.reducePasskeyChecked, }, { Event: session.TokenSetType, Reduce: p.reduceTokenSet, }, { Event: session.MetadataSetType, Reduce: p.reduceMetadataSet, }, { Event: session.TerminateType, Reduce: p.reduceSessionTerminated, }, }, }, { Aggregate: instance.AggregateType, EventRedusers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(SMSColumnInstanceID), }, }, }, { Aggregate: user.AggregateType, EventRedusers: []handler.EventReducer{ { Event: user.HumanPasswordChangedType, Reduce: p.reducePasswordChanged, }, }, }, } } func (p *sessionProjection) reduceSessionAdded(event eventstore.Event) (*handler.Statement, error) { e, ok := event.(*session.AddedEvent) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Sfrgf", "reduce.wrong.event.type %s", session.AddedType) } return crdb.NewCreateStatement( e, []handler.Column{ handler.NewCol(SessionColumnID, e.Aggregate().ID), handler.NewCol(SessionColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(SessionColumnCreationDate, e.CreationDate()), handler.NewCol(SessionColumnChangeDate, e.CreationDate()), handler.NewCol(SessionColumnResourceOwner, e.Aggregate().ResourceOwner), handler.NewCol(SessionColumnDomain, e.Domain), handler.NewCol(SessionColumnState, domain.SessionStateActive), handler.NewCol(SessionColumnSequence, e.Sequence()), handler.NewCol(SessionColumnCreator, e.User), }, ), nil } func (p *sessionProjection) reduceUserChecked(event eventstore.Event) (*handler.Statement, error) { e, ok := event.(*session.UserCheckedEvent) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-saDg5", "reduce.wrong.event.type %s", session.UserCheckedType) } return crdb.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnChangeDate, e.CreationDate()), handler.NewCol(SessionColumnSequence, e.Sequence()), handler.NewCol(SessionColumnUserID, e.UserID), handler.NewCol(SessionColumnUserCheckedAt, e.CheckedAt), }, []handler.Condition{ handler.NewCond(SessionColumnID, e.Aggregate().ID), handler.NewCond(SessionColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } func (p *sessionProjection) reducePasswordChecked(event eventstore.Event) (*handler.Statement, error) { e, ok := event.(*session.PasswordCheckedEvent) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SDgrb", "reduce.wrong.event.type %s", session.PasswordCheckedType) } return crdb.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnChangeDate, e.CreationDate()), handler.NewCol(SessionColumnSequence, e.Sequence()), handler.NewCol(SessionColumnPasswordCheckedAt, e.CheckedAt), }, []handler.Condition{ handler.NewCond(SessionColumnID, e.Aggregate().ID), handler.NewCond(SessionColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } func (p *sessionProjection) reduceIntentChecked(event eventstore.Event) (*handler.Statement, error) { e, ok := event.(*session.IntentCheckedEvent) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SDgr2", "reduce.wrong.event.type %s", session.IntentCheckedType) } return crdb.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnChangeDate, e.CreationDate()), handler.NewCol(SessionColumnSequence, e.Sequence()), handler.NewCol(SessionColumnIntentCheckedAt, e.CheckedAt), }, []handler.Condition{ handler.NewCond(SessionColumnID, e.Aggregate().ID), handler.NewCond(SessionColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } func (p *sessionProjection) reducePasskeyChecked(event eventstore.Event) (*handler.Statement, error) { e, ok := event.(*session.PasskeyCheckedEvent) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-WieM4", "reduce.wrong.event.type %s", session.PasskeyCheckedType) } return crdb.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnChangeDate, e.CreationDate()), handler.NewCol(SessionColumnSequence, e.Sequence()), handler.NewCol(SessionColumnPasskeyCheckedAt, e.CheckedAt), }, []handler.Condition{ handler.NewCond(SessionColumnID, e.Aggregate().ID), handler.NewCond(SessionColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } func (p *sessionProjection) reduceTokenSet(event eventstore.Event) (*handler.Statement, error) { e, ok := event.(*session.TokenSetEvent) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SAfd3", "reduce.wrong.event.type %s", session.TokenSetType) } return crdb.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnChangeDate, e.CreationDate()), handler.NewCol(SessionColumnSequence, e.Sequence()), handler.NewCol(SessionColumnTokenID, e.TokenID), }, []handler.Condition{ handler.NewCond(SessionColumnID, e.Aggregate().ID), handler.NewCond(SessionColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } func (p *sessionProjection) reduceMetadataSet(event eventstore.Event) (*handler.Statement, error) { e, ok := event.(*session.MetadataSetEvent) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SAfd3", "reduce.wrong.event.type %s", session.MetadataSetType) } return crdb.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnChangeDate, e.CreationDate()), handler.NewCol(SessionColumnSequence, e.Sequence()), handler.NewCol(SessionColumnMetadata, e.Metadata), }, []handler.Condition{ handler.NewCond(SessionColumnID, e.Aggregate().ID), handler.NewCond(SessionColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } func (p *sessionProjection) reduceSessionTerminated(event eventstore.Event) (*handler.Statement, error) { e, ok := event.(*session.TerminateEvent) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SAftn", "reduce.wrong.event.type %s", session.TerminateType) } return crdb.NewDeleteStatement( e, []handler.Condition{ handler.NewCond(SessionColumnID, e.Aggregate().ID), handler.NewCond(SessionColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } func (p *sessionProjection) reducePasswordChanged(event eventstore.Event) (*handler.Statement, error) { e, ok := event.(*user.HumanPasswordChangedEvent) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Deg3d", "reduce.wrong.event.type %s", user.HumanPasswordChangedType) } return crdb.NewUpdateStatement( e, []handler.Column{ handler.NewCol(SessionColumnPasswordCheckedAt, nil), }, []handler.Condition{ handler.NewCond(SessionColumnUserID, e.Aggregate().ID), crdb.NewLessThanCond(SessionColumnPasswordCheckedAt, e.CreationDate()), }, ), nil }