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:
Livio Spring 2024-05-22 17:26:02 +02:00 committed by GitHub
parent cca342187b
commit fb162a7d75
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 987 additions and 1279 deletions

27
cmd/setup/26.go Normal file
View File

@ -0,0 +1,27 @@
package setup
import (
"context"
_ "embed"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/eventstore"
)
var (
//go:embed 26.sql
authUsers3 string
)
type AuthUsers3 struct {
dbClient *database.DB
}
func (mig *AuthUsers3) Execute(ctx context.Context, _ eventstore.Event) error {
_, err := mig.dbClient.ExecContext(ctx, authUsers3)
return err
}
func (mig *AuthUsers3) String() string {
return "26_auth_users3"
}

16
cmd/setup/26.sql Normal file
View File

@ -0,0 +1,16 @@
CREATE TABLE IF NOT EXISTS auth.users3 (
instance_id TEXT NOT NULL,
id TEXT NOT NULL,
resource_owner TEXT NOT NULL,
change_date TIMESTAMPTZ NULL,
password_set BOOL NULL,
password_change TIMESTAMPTZ NULL,
last_login TIMESTAMPTZ NULL,
init_required BOOL NULL,
mfa_init_skipped TIMESTAMPTZ NULL,
username_change_required BOOL NULL,
passwordless_init_required BOOL NULL,
password_init_required BOOL NULL,
PRIMARY KEY (instance_id, id)
)

View File

@ -108,6 +108,7 @@ type Steps struct {
s23CorrectGlobalUniqueConstraints *CorrectGlobalUniqueConstraints
s24AddActorToAuthTokens *AddActorToAuthTokens
s25User11AddLowerFieldsToVerifiedEmail *User11AddLowerFieldsToVerifiedEmail
s26AuthUsers3 *AuthUsers3
}
func MustNewSteps(v *viper.Viper) *Steps {

View File

@ -138,6 +138,7 @@ func Setup(config *Config, steps *Steps, masterKey string) {
steps.s23CorrectGlobalUniqueConstraints = &CorrectGlobalUniqueConstraints{dbClient: esPusherDBClient}
steps.s24AddActorToAuthTokens = &AddActorToAuthTokens{dbClient: queryDBClient}
steps.s25User11AddLowerFieldsToVerifiedEmail = &User11AddLowerFieldsToVerifiedEmail{dbClient: esPusherDBClient}
steps.s26AuthUsers3 = &AuthUsers3{dbClient: esPusherDBClient}
err = projection.Create(ctx, projectionDBClient, eventstoreClient, config.Projections, nil, nil, nil)
logging.OnError(err).Fatal("unable to start projections")
@ -175,6 +176,7 @@ func Setup(config *Config, steps *Steps, masterKey string) {
steps.s22ActiveInstancesIndex,
steps.s23CorrectGlobalUniqueConstraints,
steps.s24AddActorToAuthTokens,
steps.s26AuthUsers3,
} {
mustExecuteMigration(ctx, eventstoreClient, step, "migration failed")
}

View File

@ -1517,12 +1517,12 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
user_repo.HumanPasswordlessTokenCheckFailedType,
user_repo.HumanU2FTokenCheckSucceededType,
user_repo.HumanU2FTokenCheckFailedType:
eventData, err := user_view_model.UserSessionFromEvent(event)
userAgentID, err := user_view_model.UserAgentIDFromEvent(event)
if err != nil {
logging.WithFields("traceID", tracing.TraceIDFromCtx(ctx)).WithError(err).Debug("error getting event data")
return user_view_model.UserSessionToModel(session), nil
}
if eventData.UserAgentID != agentID {
if userAgentID != agentID {
continue
}
case user_repo.UserRemovedType:

View File

@ -2,6 +2,7 @@ package eventstore
import (
"context"
"database/sql"
"encoding/json"
"testing"
"time"
@ -75,11 +76,11 @@ type mockUser struct {
func (m *mockViewUserSession) UserSessionByIDs(string, string, string) (*user_view_model.UserSessionView, error) {
return &user_view_model.UserSessionView{
ExternalLoginVerification: m.ExternalLoginVerification,
PasswordlessVerification: m.PasswordlessVerification,
PasswordVerification: m.PasswordVerification,
SecondFactorVerification: m.SecondFactorVerification,
MultiFactorVerification: m.MultiFactorVerification,
ExternalLoginVerification: sql.NullTime{Time: m.ExternalLoginVerification},
PasswordlessVerification: sql.NullTime{Time: m.PasswordlessVerification},
PasswordVerification: sql.NullTime{Time: m.PasswordVerification},
SecondFactorVerification: sql.NullTime{Time: m.SecondFactorVerification},
MultiFactorVerification: sql.NullTime{Time: m.MultiFactorVerification},
}, nil
}
@ -90,7 +91,7 @@ func (m *mockViewUserSession) UserSessionsByAgentID(string, string) ([]*user_vie
ResourceOwner: user.ResourceOwner,
State: int32(user.SessionState),
UserID: user.UserID,
LoginName: user.LoginName,
LoginName: sql.NullString{String: user.LoginName},
}
}
return sessions, nil

View File

@ -3,8 +3,6 @@ package handler
import (
"context"
"github.com/zitadel/logging"
auth_view "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
@ -98,50 +96,84 @@ func (t *RefreshToken) Reducers() []handler.AggregateReducer {
}
func (t *RefreshToken) Reduce(event eventstore.Event) (_ *handler.Statement, err error) {
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
switch event.Type() {
case user.HumanRefreshTokenAddedType:
token := new(view_model.RefreshTokenView)
err := token.AppendEvent(event)
if err != nil {
return err
}
return t.view.PutRefreshToken(token)
case user.HumanRefreshTokenRenewedType:
e := new(user.HumanRefreshTokenRenewedEvent)
if err := event.Unmarshal(e); err != nil {
logging.WithError(err).Error("could not unmarshal event data")
return zerrors.ThrowInternal(nil, "MODEL-BHn75", "could not unmarshal data")
}
token, err := t.view.RefreshTokenByID(e.TokenID, event.Aggregate().InstanceID)
if err != nil {
return err
}
err = token.AppendEvent(event)
if err != nil {
return err
}
return t.view.PutRefreshToken(token)
case user.HumanRefreshTokenRemovedType:
e := new(user.HumanRefreshTokenRemovedEvent)
if err := event.Unmarshal(e); err != nil {
logging.WithError(err).Error("could not unmarshal event data")
return zerrors.ThrowInternal(nil, "MODEL-Bz653", "could not unmarshal data")
}
return t.view.DeleteRefreshToken(e.TokenID, event.Aggregate().InstanceID)
case user.UserLockedType,
user.UserDeactivatedType,
user.UserRemovedType:
return t.view.DeleteUserRefreshTokens(event.Aggregate().ID, event.Aggregate().InstanceID)
case instance.InstanceRemovedEventType:
return t.view.DeleteInstanceRefreshTokens(event.Aggregate().InstanceID)
case org.OrgRemovedEventType:
return t.view.DeleteOrgRefreshTokens(event)
default:
return nil
// in case anything needs to be change here check if appendEvent function needs the change as well
switch event.Type() {
case user.HumanRefreshTokenAddedType:
e, ok := event.(*user.HumanRefreshTokenAddedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "MODEL-IoF6j", "reduce.wrong.event.type %s", user.HumanRefreshTokenAddedType)
}
}), nil
columns := []handler.Column{
handler.NewCol(view_model.RefreshTokenKeyClientID, e.ClientID),
handler.NewCol(view_model.RefreshTokenKeyUserAgentID, e.UserAgentID),
handler.NewCol(view_model.RefreshTokenKeyUserID, e.Aggregate().ID),
handler.NewCol(view_model.RefreshTokenKeyInstanceID, e.Aggregate().InstanceID),
handler.NewCol(view_model.RefreshTokenKeyTokenID, e.TokenID),
handler.NewCol(view_model.RefreshTokenKeyResourceOwner, e.Aggregate().ResourceOwner),
handler.NewCol(view_model.RefreshTokenKeyCreationDate, event.CreatedAt()),
handler.NewCol(view_model.RefreshTokenKeyChangeDate, event.CreatedAt()),
handler.NewCol(view_model.RefreshTokenKeySequence, event.Sequence()),
handler.NewCol(view_model.RefreshTokenKeyAMR, e.AuthMethodsReferences),
handler.NewCol(view_model.RefreshTokenKeyAuthTime, e.AuthTime),
handler.NewCol(view_model.RefreshTokenKeyAudience, e.Audience),
handler.NewCol(view_model.RefreshTokenKeyExpiration, event.CreatedAt().Add(e.Expiration)),
handler.NewCol(view_model.RefreshTokenKeyIdleExpiration, event.CreatedAt().Add(e.IdleExpiration)),
handler.NewCol(view_model.RefreshTokenKeyScopes, e.Scopes),
handler.NewCol(view_model.RefreshTokenKeyToken, e.TokenID),
handler.NewCol(view_model.RefreshTokenKeyActor, view_model.TokenActor{TokenActor: e.Actor}),
}
return handler.NewUpsertStatement(event, columns[0:3], columns), nil
case user.HumanRefreshTokenRenewedType:
e, ok := event.(*user.HumanRefreshTokenRenewedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "MODEL-AG43hq", "reduce.wrong.event.type %s", user.HumanRefreshTokenRenewedType)
}
return handler.NewUpdateStatement(event,
[]handler.Column{
handler.NewCol(view_model.RefreshTokenKeyIdleExpiration, event.CreatedAt().Add(e.IdleExpiration)),
handler.NewCol(view_model.RefreshTokenKeyToken, e.RefreshToken),
handler.NewCol(view_model.RefreshTokenKeyChangeDate, e.CreatedAt()),
handler.NewCol(view_model.RefreshTokenKeySequence, event.Sequence()),
},
[]handler.Condition{
handler.NewCond(view_model.RefreshTokenKeyTokenID, e.TokenID),
handler.NewCond(view_model.RefreshTokenKeyInstanceID, e.Aggregate().InstanceID),
},
), nil
case user.HumanRefreshTokenRemovedType:
e, ok := event.(*user.HumanRefreshTokenRemovedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "MODEL-SFF3t", "reduce.wrong.event.type %s", user.HumanRefreshTokenRemovedType)
}
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.RefreshTokenKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.RefreshTokenKeyTokenID, e.TokenID),
},
), nil
case user.UserLockedType,
user.UserDeactivatedType,
user.UserRemovedType:
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.RefreshTokenKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.RefreshTokenKeyUserID, event.Aggregate().ID),
},
), nil
case instance.InstanceRemovedEventType:
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.RefreshTokenKeyInstanceID, event.Aggregate().InstanceID),
},
), nil
case org.OrgRemovedEventType:
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.RefreshTokenKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.RefreshTokenKeyResourceOwner, event.Aggregate().ResourceOwner),
},
), nil
default:
return handler.NewNoOpStatement(event), nil
}
}

View File

@ -3,6 +3,7 @@ package handler
import (
"context"
"github.com/muhlemmer/gu"
"github.com/zitadel/logging"
auth_view "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view"
@ -150,129 +151,189 @@ func (t *Token) Reducers() []handler.AggregateReducer {
}
func (t *Token) Reduce(event eventstore.Event) (_ *handler.Statement, err error) { //nolint:gocognit
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
switch event.Type() {
case user.UserTokenAddedType,
user.PersonalAccessTokenAddedType:
token := new(view_model.TokenView)
err := token.AppendEvent(event)
if err != nil {
return err
}
return t.view.PutToken(token)
case user.UserV1ProfileChangedType,
user.HumanProfileChangedType:
user := new(view_model.UserView)
err := user.AppendEvent(event)
if err != nil {
return err
}
tokens, err := t.view.TokensByUserID(event.Aggregate().ID, event.Aggregate().InstanceID)
if err != nil {
return err
}
for _, token := range tokens {
token.PreferredLanguage = user.PreferredLanguage
}
return t.view.PutTokens(tokens)
case user.UserV1SignedOutType,
user.HumanSignedOutType:
id, err := agentIDFromSession(event)
if err != nil {
return err
}
return t.view.DeleteSessionTokens(id, event)
case user.UserLockedType,
user.UserDeactivatedType,
user.UserRemovedType:
return t.view.DeleteUserTokens(event)
case user.UserTokenRemovedType,
user.PersonalAccessTokenRemovedType:
id, err := tokenIDFromRemovedEvent(event)
if err != nil {
return err
}
return t.view.DeleteToken(id, event.Aggregate().InstanceID)
case user.HumanRefreshTokenRemovedType:
id, err := refreshTokenIDFromRemovedEvent(event)
if err != nil {
return err
}
return t.view.DeleteTokensFromRefreshToken(id, event.Aggregate().InstanceID)
case project.ApplicationDeactivatedType,
project.ApplicationRemovedType:
application, err := applicationFromSession(event)
if err != nil {
return err
}
return t.view.DeleteApplicationTokens(event, application.AppID)
case project.ProjectDeactivatedType,
project.ProjectRemovedType:
project, err := t.getProjectByID(context.Background(), event.Aggregate().ID, event.Aggregate().InstanceID)
if err != nil {
return err
}
applicationIDs := make([]string, 0, len(project.Applications))
for _, app := range project.Applications {
if app.OIDCConfig != nil && app.OIDCConfig.ClientID != "" {
applicationIDs = append(applicationIDs, app.OIDCConfig.ClientID)
}
}
return t.view.DeleteApplicationTokens(event, applicationIDs...)
case instance.InstanceRemovedEventType:
return t.view.DeleteInstanceTokens(event)
case org.OrgRemovedEventType:
// deletes all tokens including PATs, which is expected for now
// if there is an undo of the org deletion in the future,
// we will need to have a look on how to handle the deleted PATs
return t.view.DeleteOrgTokens(event)
default:
return nil
// in case anything needs to be change here check if appendEvent function needs the change as well
switch event.Type() {
case user.UserTokenAddedType:
e, ok := event.(*user.UserTokenAddedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "MODEL-W4tnq", "reduce.wrong.event.type %s", user.UserTokenAddedType)
}
}), nil
return handler.NewCreateStatement(event,
[]handler.Column{
handler.NewCol(view_model.TokenKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCol(view_model.TokenKeyUserID, event.Aggregate().ID),
handler.NewCol(view_model.TokenKeyResourceOwner, event.Aggregate().ResourceOwner),
handler.NewCol(view_model.TokenKeyID, e.TokenID),
handler.NewCol(view_model.TokenKeyCreationDate, event.CreatedAt()),
handler.NewCol(view_model.TokenKeyChangeDate, event.CreatedAt()),
handler.NewCol(view_model.TokenKeySequence, event.Sequence()),
handler.NewCol(view_model.TokenKeyApplicationID, e.ApplicationID),
handler.NewCol(view_model.TokenKeyUserAgentID, e.UserAgentID),
handler.NewCol(view_model.TokenKeyAudience, e.Audience),
handler.NewCol(view_model.TokenKeyScopes, e.Scopes),
handler.NewCol(view_model.TokenKeyExpiration, e.Expiration),
handler.NewCol(view_model.TokenKeyPreferredLanguage, e.PreferredLanguage),
handler.NewCol(view_model.TokenKeyRefreshTokenID, e.RefreshTokenID),
handler.NewCol(view_model.TokenKeyActor, view_model.TokenActor{TokenActor: e.Actor}),
handler.NewCol(view_model.TokenKeyIsPat, false),
},
), nil
case user.PersonalAccessTokenAddedType:
e, ok := event.(*user.PersonalAccessTokenAddedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "MODEL-zF3rb", "reduce.wrong.event.type %s", user.PersonalAccessTokenAddedType)
}
return handler.NewCreateStatement(event,
[]handler.Column{
handler.NewCol(view_model.TokenKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCol(view_model.TokenKeyUserID, event.Aggregate().ID),
handler.NewCol(view_model.TokenKeyResourceOwner, event.Aggregate().ResourceOwner),
handler.NewCol(view_model.TokenKeyID, e.TokenID),
handler.NewCol(view_model.TokenKeyCreationDate, event.CreatedAt()),
handler.NewCol(view_model.TokenKeyChangeDate, event.CreatedAt()),
handler.NewCol(view_model.TokenKeySequence, event.Sequence()),
handler.NewCol(view_model.TokenKeyScopes, e.Scopes),
handler.NewCol(view_model.TokenKeyExpiration, e.Expiration),
handler.NewCol(view_model.TokenKeyIsPat, true),
},
), nil
case user.UserV1ProfileChangedType,
user.HumanProfileChangedType:
e, ok := event.(*user.HumanProfileChangedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "MODEL-ASF2t", "reduce.wrong.event.type %s", user.HumanProfileChangedType)
}
if e.PreferredLanguage == nil {
return handler.NewNoOpStatement(event), nil
}
return handler.NewUpdateStatement(event,
[]handler.Column{
handler.NewCol(view_model.TokenKeyPreferredLanguage, gu.Value(e.PreferredLanguage).String()),
handler.NewCol(view_model.TokenKeyChangeDate, event.CreatedAt()),
handler.NewCol(view_model.TokenKeySequence, event.Sequence()),
},
[]handler.Condition{
handler.NewCond(view_model.TokenKeyInstanceID, e.Aggregate().InstanceID),
handler.NewCond(view_model.TokenKeyUserID, e.Aggregate().ID),
},
), nil
case user.UserV1SignedOutType,
user.HumanSignedOutType:
e, ok := event.(*user.HumanSignedOutEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "MODEL-Wtn2q", "reduce.wrong.event.type %s", user.HumanSignedOutType)
}
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.TokenKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.TokenKeyUserID, event.Aggregate().ID),
handler.NewCond(view_model.TokenKeyUserAgentID, e.UserAgentID),
},
), nil
case user.UserLockedType,
user.UserDeactivatedType,
user.UserRemovedType:
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.TokenKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.TokenKeyUserID, event.Aggregate().ID),
},
), nil
case user.UserTokenRemovedType,
user.PersonalAccessTokenRemovedType:
var tokenID string
switch e := event.(type) {
case *user.UserTokenRemovedEvent:
tokenID = e.TokenID
case *user.PersonalAccessTokenRemovedEvent:
tokenID = e.TokenID
default:
return nil, zerrors.ThrowInvalidArgumentf(nil, "MODEL-SF3ga", "reduce.wrong.event.type %s", user.UserTokenRemovedType)
}
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.TokenKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.TokenKeyID, tokenID),
},
), nil
case user.HumanRefreshTokenRemovedType:
e, ok := event.(*user.HumanRefreshTokenRemovedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "MODEL-Sfe11", "reduce.wrong.event.type %s", user.HumanRefreshTokenRemovedType)
}
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.TokenKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.TokenKeyRefreshTokenID, e.TokenID),
},
), nil
case project.ApplicationDeactivatedType,
project.ApplicationRemovedType:
var applicationID string
switch e := event.(type) {
case *project.ApplicationDeactivatedEvent:
applicationID = e.AppID
case *project.ApplicationRemovedEvent:
applicationID = e.AppID
default:
return nil, zerrors.ThrowInvalidArgumentf(nil, "MODEL-SF3fq", "reduce.wrong.event.type %v", []eventstore.EventType{project.ApplicationDeactivatedType, project.ApplicationRemovedType})
}
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.TokenKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.TokenKeyApplicationID, applicationID),
},
), nil
case project.ProjectDeactivatedType,
project.ProjectRemovedType:
project, err := t.getProjectByID(context.Background(), event.Aggregate().ID, event.Aggregate().InstanceID)
if err != nil {
return nil, err
}
applicationIDs := make([]string, 0, len(project.Applications))
for _, app := range project.Applications {
if app.OIDCConfig != nil && app.OIDCConfig.ClientID != "" {
applicationIDs = append(applicationIDs, app.OIDCConfig.ClientID)
}
}
if len(applicationIDs) == 0 {
return handler.NewNoOpStatement(event), nil
}
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.TokenKeyInstanceID, event.Aggregate().InstanceID),
handler.NewOneOfTextCond(view_model.TokenKeyApplicationID, applicationIDs),
},
), nil
case instance.InstanceRemovedEventType:
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.TokenKeyInstanceID, event.Aggregate().InstanceID),
},
), nil
case org.OrgRemovedEventType:
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.TokenKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.TokenKeyResourceOwner, event.Aggregate().ResourceOwner),
},
), nil
default:
return handler.NewNoOpStatement(event), nil
}
}
type userAgentIDPayload struct {
ID string `json:"userAgentID"`
}
func agentIDFromSession(event eventstore.Event) (string, error) {
session := make(map[string]interface{})
if err := event.Unmarshal(&session); err != nil {
payload := new(userAgentIDPayload)
if err := event.Unmarshal(payload); err != nil {
logging.WithError(err).Error("could not unmarshal event data")
return "", zerrors.ThrowInternal(nil, "MODEL-sd325", "could not unmarshal data")
}
agentID, _ := session["userAgentID"].(string)
return agentID, nil
}
func applicationFromSession(event eventstore.Event) (*project_es_model.Application, error) {
application := new(project_es_model.Application)
if err := event.Unmarshal(application); err != nil {
logging.WithError(err).Error("could not unmarshal event data")
return nil, zerrors.ThrowInternal(nil, "MODEL-Hrw1q", "could not unmarshal data")
}
return application, nil
}
func tokenIDFromRemovedEvent(event eventstore.Event) (string, error) {
removed := make(map[string]interface{})
if err := event.Unmarshal(&removed); err != nil {
logging.WithError(err).Error("could not unmarshal event data")
return "", zerrors.ThrowInternal(nil, "MODEL-Sff32", "could not unmarshal data")
}
return removed["tokenId"].(string), nil
}
func refreshTokenIDFromRemovedEvent(event eventstore.Event) (string, error) {
removed := make(map[string]interface{})
if err := event.Unmarshal(&removed); err != nil {
logging.WithError(err).Error("could not unmarshal event data")
return "", zerrors.ThrowInternal(nil, "MODEL-Dfb3w", "could not unmarshal data")
}
return removed["tokenId"].(string), nil
return payload.ID, nil
}
func (t *Token) getProjectByID(ctx context.Context, projID, instanceID string) (*proj_model.Project, error) {

View File

@ -4,25 +4,20 @@ import (
"context"
"time"
"github.com/zitadel/zitadel/internal/api/authz"
auth_view "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
org_model "github.com/zitadel/zitadel/internal/org/model"
org_es_model "github.com/zitadel/zitadel/internal/org/repository/eventsourcing/model"
org_view "github.com/zitadel/zitadel/internal/org/repository/view"
query2 "github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/repository/org"
user_repo "github.com/zitadel/zitadel/internal/repository/user"
usr_view "github.com/zitadel/zitadel/internal/user/repository/view"
view_model "github.com/zitadel/zitadel/internal/user/repository/view/model"
"github.com/zitadel/zitadel/internal/zerrors"
)
const (
userTable = "auth.users2"
userTable = "auth.users3"
)
type User struct {
@ -58,26 +53,14 @@ func (u *User) Reducers() []handler.AggregateReducer {
{
Aggregate: user_repo.AggregateType,
EventReducers: []handler.EventReducer{
{
Event: user_repo.HumanOTPSMSAddedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanOTPSMSRemovedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanOTPEmailAddedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanOTPEmailRemovedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.MachineAddedEventType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanAddedType,
Reduce: u.ProcessUser,
@ -94,62 +77,14 @@ func (u *User) Reducers() []handler.AggregateReducer {
Event: user_repo.HumanRegisteredType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserV1ProfileChangedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserV1EmailChangedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserV1EmailVerifiedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserV1PhoneChangedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserV1PhoneVerifiedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserV1PhoneRemovedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserV1AddressChangedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserDeactivatedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserReactivatedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserLockedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserUnlockedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserV1MFAOTPAddedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserV1MFAOTPVerifiedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserV1MFAOTPRemovedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserV1MFAInitSkippedType,
Reduce: u.ProcessUser,
@ -158,86 +93,22 @@ func (u *User) Reducers() []handler.AggregateReducer {
Event: user_repo.UserV1PasswordChangedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanProfileChangedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanEmailChangedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanEmailVerifiedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanAvatarAddedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanAvatarRemovedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanPhoneChangedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanPhoneVerifiedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanPhoneRemovedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanAddressChangedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanMFAOTPAddedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanMFAOTPVerifiedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanMFAOTPRemovedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanU2FTokenAddedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanU2FTokenVerifiedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanU2FTokenRemovedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanPasswordlessTokenAddedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanPasswordlessTokenVerifiedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanPasswordlessTokenRemovedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanMFAInitSkippedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.MachineChangedEventType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.HumanPasswordChangedType,
Reduce: u.ProcessUser,
@ -266,14 +137,6 @@ func (u *User) Reducers() []handler.AggregateReducer {
Event: user_repo.HumanPasswordlessInitCodeRequestedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserDomainClaimedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserUserNameChangedType,
Reduce: u.ProcessUser,
},
{
Event: user_repo.UserRemovedType,
Reduce: u.ProcessUser,
@ -283,30 +146,6 @@ func (u *User) Reducers() []handler.AggregateReducer {
{
Aggregate: org.AggregateType,
EventReducers: []handler.EventReducer{
{
Event: org.OrgDomainVerifiedEventType,
Reduce: u.ProcessOrg,
},
{
Event: org.OrgDomainRemovedEventType,
Reduce: u.ProcessOrg,
},
{
Event: org.DomainPolicyAddedEventType,
Reduce: u.ProcessOrg,
},
{
Event: org.DomainPolicyChangedEventType,
Reduce: u.ProcessOrg,
},
{
Event: org.DomainPolicyRemovedEventType,
Reduce: u.ProcessOrg,
},
{
Event: org.OrgDomainPrimarySetEventType,
Reduce: u.ProcessOrg,
},
{
Event: org.OrgRemovedEventType,
Reduce: u.ProcessOrg,
@ -327,141 +166,124 @@ func (u *User) Reducers() []handler.AggregateReducer {
//nolint:gocognit
func (u *User) ProcessUser(event eventstore.Event) (_ *handler.Statement, err error) {
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
user := new(view_model.UserView)
switch event.Type() {
case user_repo.UserV1AddedType,
user_repo.MachineAddedEventType,
user_repo.HumanAddedType,
user_repo.UserV1RegisteredType,
user_repo.HumanRegisteredType:
err = user.AppendEvent(event)
if err != nil {
return err
}
err = u.fillLoginNames(user)
case user_repo.UserV1ProfileChangedType,
user_repo.UserV1EmailChangedType,
user_repo.UserV1EmailVerifiedType,
user_repo.UserV1PhoneChangedType,
user_repo.UserV1PhoneVerifiedType,
user_repo.UserV1PhoneRemovedType,
user_repo.UserV1AddressChangedType,
user_repo.UserDeactivatedType,
user_repo.UserReactivatedType,
user_repo.UserLockedType,
user_repo.UserUnlockedType,
user_repo.UserV1MFAOTPAddedType,
user_repo.UserV1MFAOTPVerifiedType,
user_repo.UserV1MFAOTPRemovedType,
user_repo.UserV1MFAInitSkippedType,
user_repo.UserV1PasswordChangedType,
user_repo.HumanProfileChangedType,
user_repo.HumanEmailChangedType,
user_repo.HumanEmailVerifiedType,
user_repo.HumanAvatarAddedType,
user_repo.HumanAvatarRemovedType,
user_repo.HumanPhoneChangedType,
user_repo.HumanPhoneVerifiedType,
user_repo.HumanPhoneRemovedType,
user_repo.HumanAddressChangedType,
user_repo.HumanMFAOTPAddedType,
user_repo.HumanMFAOTPVerifiedType,
user_repo.HumanMFAOTPRemovedType,
user_repo.HumanOTPSMSAddedType,
user_repo.HumanOTPSMSRemovedType,
user_repo.HumanOTPEmailAddedType,
user_repo.HumanOTPEmailRemovedType,
user_repo.HumanU2FTokenAddedType,
user_repo.HumanU2FTokenVerifiedType,
user_repo.HumanU2FTokenRemovedType,
user_repo.HumanPasswordlessTokenAddedType,
user_repo.HumanPasswordlessTokenVerifiedType,
user_repo.HumanPasswordlessTokenRemovedType,
user_repo.HumanMFAInitSkippedType,
user_repo.MachineChangedEventType,
user_repo.HumanPasswordChangedType,
user_repo.HumanInitialCodeAddedType,
user_repo.UserV1InitialCodeAddedType,
user_repo.UserV1InitializedCheckSucceededType,
user_repo.HumanInitializedCheckSucceededType,
user_repo.HumanPasswordlessInitCodeAddedType,
user_repo.HumanPasswordlessInitCodeRequestedType:
user, err = u.view.UserByID(event.Aggregate().ID, event.Aggregate().InstanceID)
if err != nil {
if !zerrors.IsNotFound(err) {
return err
}
user, err = u.userFromEventstore(event.Aggregate(), user.EventTypes())
if err != nil {
return err
}
}
err = user.AppendEvent(event)
case user_repo.UserDomainClaimedType,
user_repo.UserUserNameChangedType:
user, err = u.view.UserByID(event.Aggregate().ID, event.Aggregate().InstanceID)
if err != nil {
if !zerrors.IsNotFound(err) {
return err
}
user, err = u.userFromEventstore(event.Aggregate(), user.EventTypes())
if err != nil {
return err
}
}
err = user.AppendEvent(event)
if err != nil {
return err
}
err = u.fillLoginNames(user)
case user_repo.UserRemovedType:
return u.view.DeleteUser(event.Aggregate().ID, event.Aggregate().InstanceID, event)
default:
return nil
// in case anything needs to be change here check if appendEvent function needs the change as well
switch event.Type() {
case user_repo.UserV1AddedType,
user_repo.HumanAddedType:
e, ok := event.(*user_repo.HumanAddedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "MODEL-SDAGF", "reduce.wrong.event.type %s", user_repo.HumanAddedType)
}
if err != nil {
return err
return u.setPasswordData(event, e.Secret, e.EncodedHash), nil
case user_repo.UserV1RegisteredType,
user_repo.HumanRegisteredType:
e, ok := event.(*user_repo.HumanRegisteredEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "MODEL-AS1hz", "reduce.wrong.event.type %s", user_repo.HumanRegisteredType)
}
return u.view.PutUser(user, event)
}), nil
return u.setPasswordData(event, e.Secret, e.EncodedHash), nil
case user_repo.UserV1PasswordChangedType,
user_repo.HumanPasswordChangedType:
e, ok := event.(*user_repo.HumanPasswordChangedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "MODEL-Gd31w", "reduce.wrong.event.type %s", user_repo.HumanPasswordChangedType)
}
return u.setPasswordData(event, e.Secret, e.EncodedHash), nil
case user_repo.UserV1PhoneRemovedType,
user_repo.HumanPhoneRemovedType,
user_repo.UserV1MFAOTPVerifiedType,
user_repo.HumanMFAOTPVerifiedType,
user_repo.HumanOTPSMSRemovedType,
user_repo.HumanOTPEmailRemovedType,
user_repo.HumanU2FTokenVerifiedType:
return handler.NewUpdateStatement(event,
[]handler.Column{
handler.NewCol(view_model.UserKeyMFAInitSkipped, time.Time{}),
handler.NewCol(view_model.UserKeyChangeDate, event.CreatedAt()),
},
[]handler.Condition{
handler.NewCond(view_model.UserKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.UserKeyUserID, event.Aggregate().ID),
}), nil
case user_repo.UserV1MFAInitSkippedType,
user_repo.HumanMFAInitSkippedType:
return handler.NewUpdateStatement(event,
[]handler.Column{
handler.NewCol(view_model.UserKeyMFAInitSkipped, event.CreatedAt()),
handler.NewCol(view_model.UserKeyChangeDate, event.CreatedAt()),
},
[]handler.Condition{
handler.NewCond(view_model.UserKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.UserKeyUserID, event.Aggregate().ID),
}), nil
case user_repo.UserV1InitialCodeAddedType,
user_repo.HumanInitialCodeAddedType:
return handler.NewUpdateStatement(event,
[]handler.Column{
handler.NewCol(view_model.UserKeyInitRequired, true),
handler.NewCol(view_model.UserKeyChangeDate, event.CreatedAt()),
},
[]handler.Condition{
handler.NewCond(view_model.UserKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.UserKeyUserID, event.Aggregate().ID),
}), nil
case user_repo.UserV1InitializedCheckSucceededType,
user_repo.HumanInitializedCheckSucceededType:
return handler.NewUpdateStatement(event,
[]handler.Column{
handler.NewCol(view_model.UserKeyInitRequired, false),
handler.NewCol(view_model.UserKeyChangeDate, event.CreatedAt()),
},
[]handler.Condition{
handler.NewCond(view_model.UserKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.UserKeyUserID, event.Aggregate().ID),
}), nil
case user_repo.HumanPasswordlessInitCodeAddedType,
user_repo.HumanPasswordlessInitCodeRequestedType:
return handler.NewUpdateStatement(event,
[]handler.Column{
handler.NewCol(view_model.UserKeyPasswordlessInitRequired, true),
handler.NewCol(view_model.UserKeyPasswordInitRequired, false),
handler.NewCol(view_model.UserKeyChangeDate, event.CreatedAt()),
},
[]handler.Condition{
handler.NewCond(view_model.UserKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.UserKeyUserID, event.Aggregate().ID),
handler.NewCond(view_model.UserKeyPasswordSet, false),
}), nil
case user_repo.UserRemovedType:
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.UserKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.UserKeyUserID, event.Aggregate().ID),
}), nil
default:
return handler.NewNoOpStatement(event), nil
}
}
func (u *User) fillLoginNames(user *view_model.UserView) (err error) {
userLoginMustBeDomain, primaryDomain, domains, err := u.loginNameInformation(context.Background(), user.ResourceOwner, user.InstanceID)
if err != nil {
return err
func (u *User) setPasswordData(event eventstore.Event, secret *crypto.CryptoValue, hash string) *handler.Statement {
set := secret != nil || hash != ""
columns := []handler.Column{
handler.NewCol(view_model.UserKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCol(view_model.UserKeyUserID, event.Aggregate().ID),
handler.NewCol(view_model.UserKeyResourceOwner, event.Aggregate().ResourceOwner),
handler.NewCol(view_model.UserKeyChangeDate, event.CreatedAt()),
handler.NewCol(view_model.UserKeyPasswordSet, set),
handler.NewCol(view_model.UserKeyPasswordInitRequired, !set),
handler.NewCol(view_model.UserKeyPasswordChange, event.CreatedAt()),
}
user.SetLoginNames(userLoginMustBeDomain, domains)
user.PreferredLoginName = user.GenerateLoginName(primaryDomain, userLoginMustBeDomain)
return nil
return handler.NewUpsertStatement(event, columns[0:2], columns)
}
func (u *User) ProcessOrg(event eventstore.Event) (_ *handler.Statement, err error) {
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
switch event.Type() {
case org.OrgDomainVerifiedEventType,
org.OrgDomainRemovedEventType,
org.DomainPolicyAddedEventType,
org.DomainPolicyChangedEventType,
org.DomainPolicyRemovedEventType:
return u.fillLoginNamesOnOrgUsers(event)
case org.OrgDomainPrimarySetEventType:
return u.fillPreferredLoginNamesOnOrgUsers(event)
case org.OrgRemovedEventType:
return u.view.UpdateOrgOwnerRemovedUsers(event)
default:
return nil
}
}), nil
}
func (u *User) ProcessInstance(event eventstore.Event) (_ *handler.Statement, err error) {
// in case anything needs to be change here check if appendEvent function needs the change as well
switch event.Type() {
case instance.InstanceRemovedEventType:
return handler.NewStatement(event,
func(ex handler.Executer, projectionName string) error {
return u.view.DeleteInstanceUsers(event)
case org.OrgRemovedEventType:
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.UserKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.UserKeyResourceOwner, event.Aggregate().ID),
},
), nil
default:
@ -469,97 +291,16 @@ func (u *User) ProcessInstance(event eventstore.Event) (_ *handler.Statement, er
}
}
func (u *User) fillLoginNamesOnOrgUsers(event eventstore.Event) error {
userLoginMustBeDomain, _, domains, err := u.loginNameInformation(context.Background(), event.Aggregate().ResourceOwner, event.Aggregate().InstanceID)
if err != nil {
return err
func (u *User) ProcessInstance(event eventstore.Event) (_ *handler.Statement, err error) {
// in case anything needs to be change here check if appendEvent function needs the change as well
switch event.Type() {
case instance.InstanceRemovedEventType:
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.UserKeyInstanceID, event.Aggregate().InstanceID),
},
), nil
default:
return handler.NewNoOpStatement(event), nil
}
users, err := u.view.UsersByOrgID(event.Aggregate().ID, event.Aggregate().InstanceID)
if err != nil {
return err
}
for _, user := range users {
user.SetLoginNames(userLoginMustBeDomain, domains)
}
return u.view.PutUsers(users, event)
}
func (u *User) fillPreferredLoginNamesOnOrgUsers(event eventstore.Event) error {
userLoginMustBeDomain, primaryDomain, _, err := u.loginNameInformation(context.Background(), event.Aggregate().ResourceOwner, event.Aggregate().InstanceID)
if err != nil {
return err
}
if !userLoginMustBeDomain {
return nil
}
users, err := u.view.UsersByOrgID(event.Aggregate().ID, event.Aggregate().InstanceID)
if err != nil {
return err
}
for _, user := range users {
user.PreferredLoginName = user.GenerateLoginName(primaryDomain, userLoginMustBeDomain)
}
return u.view.PutUsers(users, event)
}
func (u *User) getOrgByID(ctx context.Context, orgID, instanceID string) (*org_model.Org, error) {
query, err := org_view.OrgByIDQuery(orgID, instanceID, 0)
if err != nil {
return nil, err
}
esOrg := &org_es_model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: orgID,
},
}
events, err := u.es.Filter(ctx, query)
if err != nil {
return nil, err
}
if err = esOrg.AppendEvents(events...); err != nil {
return nil, err
}
if esOrg.Sequence == 0 {
return nil, zerrors.ThrowNotFound(nil, "EVENT-3m9vs", "Errors.Org.NotFound")
}
return org_es_model.OrgToModel(esOrg), nil
}
func (u *User) loginNameInformation(ctx context.Context, orgID string, instanceID string) (userLoginMustBeDomain bool, primaryDomain string, domains []*org_model.OrgDomain, err error) {
org, err := u.getOrgByID(ctx, orgID, instanceID)
if err != nil {
return false, "", nil, err
}
primaryDomain, err = org.GetPrimaryDomain()
if err != nil {
return false, "", nil, err
}
if org.DomainPolicy != nil {
return org.DomainPolicy.UserLoginMustBeDomain, primaryDomain, org.Domains, nil
}
policy, err := u.queries.DefaultDomainPolicy(authz.WithInstanceID(ctx, org.InstanceID))
if err != nil {
return false, "", nil, err
}
return policy.UserLoginMustBeDomain, primaryDomain, org.Domains, nil
}
func (u *User) userFromEventstore(agg *eventstore.Aggregate, eventTypes []eventstore.EventType) (*view_model.UserView, error) {
query, err := usr_view.UserByIDQuery(agg.ID, agg.InstanceID, time.Time{}, eventTypes)
if err != nil {
return nil, err
}
events, err := u.es.Filter(context.Background(), query)
if err != nil {
return nil, err
}
user := &view_model.UserView{}
for _, e := range events {
if err = user.AppendEvent(e); err != nil {
return nil, err
}
}
return user, nil
}

View File

@ -12,8 +12,8 @@ import (
"github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/repository/org"
"github.com/zitadel/zitadel/internal/repository/user"
es_model "github.com/zitadel/zitadel/internal/user/repository/eventsourcing/model"
view_model "github.com/zitadel/zitadel/internal/user/repository/view/model"
"github.com/zitadel/zitadel/internal/zerrors"
)
const (
@ -187,64 +187,160 @@ func (s *UserSession) Reducers() []handler.AggregateReducer {
}
}
func sessionColumns(event eventstore.Event, columns ...handler.Column) ([]handler.Column, error) {
userAgent, err := agentIDFromSession(event)
if err != nil {
return nil, err
}
return append([]handler.Column{
handler.NewCol(view_model.UserSessionKeyUserAgentID, userAgent),
handler.NewCol(view_model.UserSessionKeyUserID, event.Aggregate().ID),
handler.NewCol(view_model.UserSessionKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCol(view_model.UserSessionKeyCreationDate, handler.OnlySetValueOnInsert(userSessionTable, event.CreatedAt())),
handler.NewCol(view_model.UserSessionKeyChangeDate, event.CreatedAt()),
handler.NewCol(view_model.UserSessionKeyResourceOwner, event.Aggregate().ResourceOwner),
handler.NewCol(view_model.UserSessionKeySequence, event.Sequence()),
}, columns...), nil
}
func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err error) {
// in case anything needs to be change here check if appendEvent function needs the change as well
switch event.Type() {
case user.UserV1PasswordCheckSucceededType,
user.UserV1PasswordCheckFailedType,
user.UserV1MFAOTPCheckSucceededType,
user.UserV1MFAOTPCheckFailedType,
user.UserV1SignedOutType,
user.HumanPasswordCheckSucceededType,
user.HumanPasswordCheckFailedType,
user.UserIDPLoginCheckSucceededType,
user.HumanMFAOTPCheckSucceededType,
user.HumanPasswordCheckSucceededType:
columns, err := sessionColumns(event,
handler.NewCol(view_model.UserSessionKeyPasswordVerification, event.CreatedAt()),
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
)
if err != nil {
return nil, err
}
return handler.NewUpsertStatement(event, columns[0:3], columns), nil
case user.UserV1PasswordCheckFailedType,
user.HumanPasswordCheckFailedType:
columns, err := sessionColumns(event,
handler.NewCol(view_model.UserSessionKeyPasswordVerification, time.Time{}),
)
if err != nil {
return nil, err
}
return handler.NewUpsertStatement(event, columns[0:3], columns), nil
case user.UserV1MFAOTPCheckSucceededType,
user.HumanMFAOTPCheckSucceededType:
columns, err := sessionColumns(event,
handler.NewCol(view_model.UserSessionKeySecondFactorVerification, event.CreatedAt()),
handler.NewCol(view_model.UserSessionKeySecondFactorVerificationType, domain.MFATypeTOTP),
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
)
if err != nil {
return nil, err
}
return handler.NewUpsertStatement(event, columns[0:3], columns), nil
case user.UserV1MFAOTPCheckFailedType,
user.HumanMFAOTPCheckFailedType,
user.HumanU2FTokenCheckSucceededType,
user.HumanU2FTokenCheckFailedType,
user.HumanPasswordlessTokenCheckSucceededType,
user.HumanPasswordlessTokenCheckFailedType,
user.HumanU2FTokenCheckFailedType:
columns, err := sessionColumns(event,
handler.NewCol(view_model.UserSessionKeySecondFactorVerification, time.Time{}),
)
if err != nil {
return nil, err
}
return handler.NewUpsertStatement(event, columns[0:3], columns), nil
case user.UserV1SignedOutType,
user.HumanSignedOutType:
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
var session *view_model.UserSessionView
eventData, err := view_model.UserSessionFromEvent(event)
if err != nil {
return err
}
session, err = u.view.UserSessionByIDs(eventData.UserAgentID, event.Aggregate().ID, event.Aggregate().InstanceID)
if err != nil {
if !zerrors.IsNotFound(err) {
return err
}
session = &view_model.UserSessionView{
CreationDate: event.CreatedAt(),
ResourceOwner: event.Aggregate().ResourceOwner,
UserAgentID: eventData.UserAgentID,
UserID: event.Aggregate().ID,
State: int32(domain.UserSessionStateActive),
InstanceID: event.Aggregate().InstanceID,
}
}
return u.updateSession(session, event)
}), nil
columns, err := sessionColumns(event,
handler.NewCol(view_model.UserSessionKeyPasswordlessVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeyPasswordVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeySecondFactorVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeySecondFactorVerificationType, domain.MFALevelNotSetUp),
handler.NewCol(view_model.UserSessionKeyMultiFactorVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeyMultiFactorVerificationType, domain.MFALevelNotSetUp),
handler.NewCol(view_model.UserSessionKeyExternalLoginVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateTerminated),
)
if err != nil {
return nil, err
}
return handler.NewUpsertStatement(event, columns[0:3], columns), nil
case user.UserIDPLoginCheckSucceededType:
data := new(es_model.AuthRequest)
err := data.SetData(event)
if err != nil {
return nil, err
}
columns, err := sessionColumns(event,
handler.NewCol(view_model.UserSessionKeyExternalLoginVerification, event.CreatedAt()),
handler.NewCol(view_model.UserSessionKeySelectedIDPConfigID, data.SelectedIDPConfigID),
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
)
if err != nil {
return nil, err
}
return handler.NewUpsertStatement(event, columns[0:3], columns), nil
case user.HumanU2FTokenCheckSucceededType:
data := new(es_model.AuthRequest)
err := data.SetData(event)
if err != nil {
return nil, err
}
columns, err := sessionColumns(event,
handler.NewCol(view_model.UserSessionKeySecondFactorVerification, event.CreatedAt()),
handler.NewCol(view_model.UserSessionKeySecondFactorVerificationType, domain.MFATypeU2F),
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
)
if err != nil {
return nil, err
}
return handler.NewUpsertStatement(event, columns[0:3], columns), nil
case user.HumanPasswordlessTokenCheckSucceededType:
data := new(es_model.AuthRequest)
err := data.SetData(event)
if err != nil {
return nil, err
}
columns, err := sessionColumns(event,
handler.NewCol(view_model.UserSessionKeyPasswordlessVerification, event.CreatedAt()),
handler.NewCol(view_model.UserSessionKeyMultiFactorVerification, event.CreatedAt()),
handler.NewCol(view_model.UserSessionKeyMultiFactorVerificationType, domain.MFATypeU2FUserVerification),
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
)
if err != nil {
return nil, err
}
return handler.NewUpsertStatement(event, columns[0:3], columns), nil
case user.HumanPasswordlessTokenCheckFailedType:
data := new(es_model.AuthRequest)
err := data.SetData(event)
if err != nil {
return nil, err
}
columns, err := sessionColumns(event,
handler.NewCol(view_model.UserSessionKeyPasswordlessVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeyMultiFactorVerification, time.Time{}),
)
if err != nil {
return nil, err
}
return handler.NewUpsertStatement(event, columns[0:3], columns), nil
case user.UserLockedType,
user.UserDeactivatedType:
return handler.NewUpdateStatement(event,
[]handler.Column{
handler.NewCol("passwordless_verification", time.Time{}),
handler.NewCol("password_verification", time.Time{}),
handler.NewCol("second_factor_verification", time.Time{}),
handler.NewCol("second_factor_verification_type", domain.MFALevelNotSetUp),
handler.NewCol("multi_factor_verification", time.Time{}),
handler.NewCol("multi_factor_verification_type", domain.MFALevelNotSetUp),
handler.NewCol("external_login_verification", time.Time{}),
handler.NewCol("state", domain.UserSessionStateTerminated),
handler.NewCol("change_date", event.CreatedAt()),
handler.NewCol("sequence", event.Sequence()),
handler.NewCol(view_model.UserSessionKeyPasswordlessVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeyPasswordVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeySecondFactorVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeySecondFactorVerificationType, domain.MFALevelNotSetUp),
handler.NewCol(view_model.UserSessionKeyMultiFactorVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeyMultiFactorVerificationType, domain.MFALevelNotSetUp),
handler.NewCol(view_model.UserSessionKeyExternalLoginVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateTerminated),
handler.NewCol(view_model.UserSessionKeyChangeDate, event.CreatedAt()),
handler.NewCol(view_model.UserSessionKeySequence, event.Sequence()),
},
[]handler.Condition{
handler.NewCond("instance_id", event.Aggregate().InstanceID),
handler.NewCond("user_id", event.Aggregate().ID),
handler.Not(handler.NewCond("state", domain.UserSessionStateTerminated)),
handler.NewCond(view_model.UserSessionKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.UserSessionKeyUserID, event.Aggregate().ID),
handler.Not(handler.NewCond(view_model.UserSessionKeyState, domain.UserSessionStateTerminated)),
},
), nil
case user.UserV1PasswordChangedType,
@ -256,26 +352,26 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
return handler.NewMultiStatement(event,
handler.AddUpdateStatement(
[]handler.Column{
handler.NewCol("password_verification", event.CreatedAt()),
handler.NewCol("change_date", event.CreatedAt()),
handler.NewCol("sequence", event.Sequence()),
handler.NewCol(view_model.UserSessionKeyPasswordVerification, event.CreatedAt()),
handler.NewCol(view_model.UserSessionKeyChangeDate, event.CreatedAt()),
handler.NewCol(view_model.UserSessionKeySequence, event.Sequence()),
},
[]handler.Condition{
handler.NewCond("instance_id", event.Aggregate().InstanceID),
handler.NewCond("user_id", event.Aggregate().ID),
handler.NewCond("user_agent_id", userAgent),
handler.NewCond(view_model.UserSessionKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.UserSessionKeyUserID, event.Aggregate().ID),
handler.NewCond(view_model.UserSessionKeyUserAgentID, userAgent),
}),
handler.AddUpdateStatement(
[]handler.Column{
handler.NewCol("password_verification", time.Time{}),
handler.NewCol("change_date", event.CreatedAt()),
handler.NewCol("sequence", event.Sequence()),
handler.NewCol(view_model.UserSessionKeyPasswordVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeyChangeDate, event.CreatedAt()),
handler.NewCol(view_model.UserSessionKeySequence, event.Sequence()),
},
[]handler.Condition{
handler.NewCond("instance_id", event.Aggregate().InstanceID),
handler.NewCond("user_id", event.Aggregate().ID),
handler.Not(handler.NewCond("user_agent_id", userAgent)),
handler.Not(handler.NewCond("state", domain.UserSessionStateTerminated)),
handler.NewCond(view_model.UserSessionKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.UserSessionKeyUserID, event.Aggregate().ID),
handler.Not(handler.NewCond(view_model.UserSessionKeyUserAgentID, userAgent)),
handler.Not(handler.NewCond(view_model.UserSessionKeyState, domain.UserSessionStateTerminated)),
}),
), nil
case user.UserV1MFAOTPRemovedType,
@ -283,84 +379,77 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
user.HumanU2FTokenRemovedType:
return handler.NewUpdateStatement(event,
[]handler.Column{
handler.NewCol("second_factor_verification", time.Time{}),
handler.NewCol("change_date", event.CreatedAt()),
handler.NewCol("sequence", event.Sequence()),
handler.NewCol(view_model.UserSessionKeySecondFactorVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeyChangeDate, event.CreatedAt()),
handler.NewCol(view_model.UserSessionKeySequence, event.Sequence()),
},
[]handler.Condition{
handler.NewCond("instance_id", event.Aggregate().InstanceID),
handler.NewCond("user_id", event.Aggregate().ID),
handler.Not(handler.NewCond("state", domain.UserSessionStateTerminated)),
handler.NewCond(view_model.UserSessionKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.UserSessionKeyUserID, event.Aggregate().ID),
handler.Not(handler.NewCond(view_model.UserSessionKeyState, domain.UserSessionStateTerminated)),
},
), nil
case user.UserIDPLinkRemovedType,
user.UserIDPLinkCascadeRemovedType:
return handler.NewUpdateStatement(event,
[]handler.Column{
handler.NewCol("external_login_verification", time.Time{}),
handler.NewCol("selected_idp_config_id", ""),
handler.NewCol("change_date", event.CreatedAt()),
handler.NewCol("sequence", event.Sequence()),
handler.NewCol(view_model.UserSessionKeyExternalLoginVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeySelectedIDPConfigID, ""),
handler.NewCol(view_model.UserSessionKeyChangeDate, event.CreatedAt()),
handler.NewCol(view_model.UserSessionKeySequence, event.Sequence()),
},
[]handler.Condition{
handler.NewCond("instance_id", event.Aggregate().InstanceID),
handler.NewCond("user_id", event.Aggregate().ID),
handler.Not(handler.NewCond("selected_idp_config_id", "")),
handler.NewCond(view_model.UserSessionKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.UserSessionKeyUserID, event.Aggregate().ID),
handler.Not(handler.NewCond(view_model.UserSessionKeySelectedIDPConfigID, "")),
},
), nil
case user.HumanPasswordlessTokenRemovedType:
return handler.NewUpdateStatement(event,
[]handler.Column{
handler.NewCol("passwordless_verification", time.Time{}),
handler.NewCol("multi_factor_verification", time.Time{}),
handler.NewCol("change_date", event.CreatedAt()),
handler.NewCol("sequence", event.Sequence()),
handler.NewCol(view_model.UserSessionKeyPasswordlessVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeyMultiFactorVerification, time.Time{}),
handler.NewCol(view_model.UserSessionKeyChangeDate, event.CreatedAt()),
handler.NewCol(view_model.UserSessionKeySequence, event.Sequence()),
},
[]handler.Condition{
handler.NewCond("instance_id", event.Aggregate().InstanceID),
handler.NewCond("user_id", event.Aggregate().ID),
handler.Not(handler.NewCond("state", domain.UserSessionStateTerminated)),
handler.NewCond(view_model.UserSessionKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.UserSessionKeyUserID, event.Aggregate().ID),
handler.Not(handler.NewCond(view_model.UserSessionKeyState, domain.UserSessionStateTerminated)),
},
), nil
case user.UserRemovedType:
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
return u.view.DeleteUserSessions(event.Aggregate().ID, event.Aggregate().InstanceID)
}), nil
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.UserSessionKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.UserSessionKeyUserID, event.Aggregate().ID),
},
), nil
case user.HumanRegisteredType:
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
eventData, err := view_model.UserSessionFromEvent(event)
if err != nil {
return err
}
session := &view_model.UserSessionView{
CreationDate: event.CreatedAt(),
ResourceOwner: event.Aggregate().ResourceOwner,
UserAgentID: eventData.UserAgentID,
UserID: event.Aggregate().ID,
State: int32(domain.UserSessionStateActive),
InstanceID: event.Aggregate().InstanceID,
PasswordVerification: event.CreatedAt(),
}
return u.updateSession(session, event)
}), nil
columns, err := sessionColumns(event,
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
handler.NewCol(view_model.UserSessionKeyPasswordVerification, event.CreatedAt()),
)
if err != nil {
return nil, err
}
return handler.NewCreateStatement(event,
columns,
), nil
case instance.InstanceRemovedEventType:
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
return u.view.DeleteInstanceUserSessions(event.Aggregate().InstanceID)
}), nil
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.UserSessionKeyInstanceID, event.Aggregate().InstanceID),
},
), nil
case org.OrgRemovedEventType:
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
return u.view.DeleteOrgUserSessions(event)
}), nil
return handler.NewDeleteStatement(event,
[]handler.Condition{
handler.NewCond(view_model.UserSessionKeyInstanceID, event.Aggregate().InstanceID),
handler.NewCond(view_model.UserSessionKeyResourceOwner, event.Aggregate().ResourceOwner),
},
), nil
default:
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
return nil
}), nil
return handler.NewNoOpStatement(event), nil
}
}
func (u *UserSession) updateSession(session *view_model.UserSessionView, event eventstore.Event) error {
if err := session.AppendEvent(event); err != nil {
return err
}
return u.view.PutUserSession(session)
}

View File

@ -4,13 +4,10 @@ import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
"github.com/zitadel/zitadel/internal/query"
user_model "github.com/zitadel/zitadel/internal/user/model"
usr_view "github.com/zitadel/zitadel/internal/user/repository/view"
"github.com/zitadel/zitadel/internal/user/repository/view/model"
"github.com/zitadel/zitadel/internal/zerrors"
)
const (
@ -29,54 +26,6 @@ func (v *View) SearchRefreshTokens(request *user_model.RefreshTokenSearchRequest
return usr_view.SearchRefreshTokens(v.Db, refreshTokenTable, request)
}
func (v *View) PutRefreshToken(token *model.RefreshTokenView) error {
return usr_view.PutRefreshToken(v.Db, refreshTokenTable, token)
}
func (v *View) PutRefreshTokens(token []*model.RefreshTokenView) error {
return usr_view.PutRefreshTokens(v.Db, refreshTokenTable, token...)
}
func (v *View) DeleteRefreshToken(tokenID, instanceID string) error {
err := usr_view.DeleteRefreshToken(v.Db, refreshTokenTable, tokenID, instanceID)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) DeleteUserRefreshTokens(userID, instanceID string) error {
err := usr_view.DeleteUserRefreshTokens(v.Db, refreshTokenTable, userID, instanceID)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) DeleteApplicationRefreshTokens(event *models.Event, ids ...string) error {
err := usr_view.DeleteApplicationTokens(v.Db, refreshTokenTable, event.InstanceID, ids)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) DeleteInstanceRefreshTokens(instanceID string) error {
err := usr_view.DeleteInstanceRefreshTokens(v.Db, refreshTokenTable, instanceID)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) DeleteOrgRefreshTokens(event eventstore.Event) error {
err := usr_view.DeleteOrgRefreshTokens(v.Db, refreshTokenTable, event.Aggregate().InstanceID, event.Aggregate().ResourceOwner)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) GetLatestRefreshTokenSequence(ctx context.Context) (_ *query.CurrentState, err error) {
q := &query.CurrentStateSearchQueries{
Queries: make([]query.SearchQuery, 2),

View File

@ -3,12 +3,10 @@ package view
import (
"context"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
usr_view "github.com/zitadel/zitadel/internal/user/repository/view"
"github.com/zitadel/zitadel/internal/user/repository/view/model"
"github.com/zitadel/zitadel/internal/zerrors"
)
const (
@ -23,70 +21,6 @@ func (v *View) TokensByUserID(userID, instanceID string) ([]*model.TokenView, er
return usr_view.TokensByUserID(v.Db, tokenTable, userID, instanceID)
}
func (v *View) PutToken(token *model.TokenView) error {
return usr_view.PutToken(v.Db, tokenTable, token)
}
func (v *View) PutTokens(token []*model.TokenView) error {
return usr_view.PutTokens(v.Db, tokenTable, token...)
}
func (v *View) DeleteToken(tokenID, instanceID string) error {
err := usr_view.DeleteToken(v.Db, tokenTable, tokenID, instanceID)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) DeleteSessionTokens(agentID string, event eventstore.Event) error {
err := usr_view.DeleteSessionTokens(v.Db, tokenTable, agentID, event.Aggregate().ID, event.Aggregate().InstanceID)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) DeleteUserTokens(event eventstore.Event) error {
err := usr_view.DeleteUserTokens(v.Db, tokenTable, event.Aggregate().ID, event.Aggregate().InstanceID)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) DeleteApplicationTokens(event eventstore.Event, ids ...string) error {
err := usr_view.DeleteApplicationTokens(v.Db, tokenTable, event.Aggregate().InstanceID, ids)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) DeleteTokensFromRefreshToken(refreshTokenID, instanceID string) error {
err := usr_view.DeleteTokensFromRefreshToken(v.Db, tokenTable, refreshTokenID, instanceID)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) DeleteInstanceTokens(event eventstore.Event) error {
err := usr_view.DeleteInstanceTokens(v.Db, tokenTable, event.Aggregate().InstanceID)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) DeleteOrgTokens(event eventstore.Event) error {
err := usr_view.DeleteOrgTokens(v.Db, tokenTable, event.Aggregate().InstanceID, event.Aggregate().ResourceOwner)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) GetLatestTokenSequence(ctx context.Context, instanceID string) (_ *query.CurrentState, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()

View File

@ -5,7 +5,6 @@ import (
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/query"
usr_model "github.com/zitadel/zitadel/internal/user/model"
"github.com/zitadel/zitadel/internal/user/repository/view"
@ -14,7 +13,7 @@ import (
)
const (
userTable = "auth.users2"
userTable = "auth.users3"
)
func (v *View) UserByID(userID, instanceID string) (*model.UserView, error) {
@ -142,42 +141,6 @@ func (v *View) userByID(ctx context.Context, instanceID string, queries ...query
return user, nil
}
func (v *View) UsersByOrgID(orgID, instanceID string) ([]*model.UserView, error) {
return view.UsersByOrgID(v.Db, userTable, orgID, instanceID)
}
func (v *View) PutUser(user *model.UserView, event eventstore.Event) error {
return view.PutUser(v.Db, userTable, user)
}
func (v *View) PutUsers(users []*model.UserView, event eventstore.Event) error {
return view.PutUsers(v.Db, userTable, users...)
}
func (v *View) DeleteUser(userID, instanceID string, event eventstore.Event) error {
err := view.DeleteUser(v.Db, userTable, userID, instanceID)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) DeleteInstanceUsers(event eventstore.Event) error {
err := view.DeleteInstanceUsers(v.Db, userTable, event.Aggregate().InstanceID)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) UpdateOrgOwnerRemovedUsers(event eventstore.Event) error {
err := view.UpdateOrgOwnerRemovedUsers(v.Db, userTable, event.Aggregate().InstanceID, event.Aggregate().ID)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) GetLatestUserSequence(ctx context.Context, instanceID string) (_ *query.CurrentState, err error) {
q := &query.CurrentStateSearchQueries{
Queries: make([]query.SearchQuery, 2),

View File

@ -3,11 +3,9 @@ package view
import (
"context"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/user/repository/view"
"github.com/zitadel/zitadel/internal/user/repository/view/model"
"github.com/zitadel/zitadel/internal/zerrors"
)
const (
@ -22,34 +20,6 @@ func (v *View) UserSessionsByAgentID(agentID, instanceID string) ([]*model.UserS
return view.UserSessionsByAgentID(v.client, agentID, instanceID)
}
func (v *View) PutUserSession(userSession *model.UserSessionView) error {
return view.PutUserSession(v.Db, userSessionTable, userSession)
}
func (v *View) DeleteUserSessions(userID, instanceID string) error {
err := view.DeleteUserSessions(v.Db, userSessionTable, userID, instanceID)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) DeleteInstanceUserSessions(instanceID string) error {
err := view.DeleteInstanceUserSessions(v.Db, userSessionTable, instanceID)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) DeleteOrgUserSessions(event eventstore.Event) error {
err := view.DeleteOrgUserSessions(v.Db, userSessionTable, event.Aggregate().InstanceID, event.Aggregate().ResourceOwner)
if err != nil && !zerrors.IsNotFound(err) {
return err
}
return nil
}
func (v *View) GetLatestUserSessionSequence(ctx context.Context, instanceID string) (_ *query.CurrentState, err error) {
q := &query.CurrentStateSearchQueries{
Queries: make([]query.SearchQuery, 2),

View File

@ -565,6 +565,13 @@ func Not(condition Condition) Condition {
}
}
// NewOneOfTextCond returns a Condition that checks if the column that stores a text is one of the given values
func NewOneOfTextCond(column string, values []string) Condition {
return func(param string) (string, []any) {
return column + " = ANY(" + param + ")", []any{database.TextArray[string](values)}
}
}
type Executer interface {
Exec(string, ...interface{}) (sql.Result, error)
}

View File

@ -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

View File

@ -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
}

View File

@ -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 != ""

View File

@ -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)
}

View File

@ -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 {

View File

@ -42,74 +42,9 @@ func RefreshTokensByUserID(db *gorm.DB, table, userID, instanceID string) ([]*us
return tokens, err
}
func PutRefreshToken(db *gorm.DB, table string, token *usr_model.RefreshTokenView) error {
save := repository.PrepareSaveOnConflict(table,
[]string{"client_id", "user_agent_id", "user_id"},
[]string{"id", "creation_date", "change_date", "token", "auth_time", "idle_expiration", "expiration", "sequence", "scopes", "audience", "amr"},
)
return save(db, token)
}
func PutRefreshTokens(db *gorm.DB, table string, tokens ...*usr_model.RefreshTokenView) error {
save := repository.PrepareBulkSave(table)
t := make([]interface{}, len(tokens))
for i, token := range tokens {
t[i] = token
}
return save(db, t...)
}
func SearchRefreshTokens(db *gorm.DB, table string, req *model.RefreshTokenSearchRequest) ([]*usr_model.RefreshTokenView, uint64, error) {
tokens := make([]*usr_model.RefreshTokenView, 0)
query := repository.PrepareSearchQuery(table, usr_model.RefreshTokenSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries})
count, err := query(db, &tokens)
return tokens, count, err
}
func DeleteRefreshToken(db *gorm.DB, table, tokenID, instanceID string) error {
delete := repository.PrepareDeleteByKeys(table,
repository.Key{usr_model.RefreshTokenSearchKey(model.RefreshTokenSearchKeyRefreshTokenID), tokenID},
repository.Key{usr_model.RefreshTokenSearchKey(model.RefreshTokenSearchKeyInstanceID), instanceID},
)
return delete(db)
}
func DeleteSessionRefreshTokens(db *gorm.DB, table, agentID, userID string) error {
delete := repository.PrepareDeleteByKeys(table,
repository.Key{Key: usr_model.RefreshTokenSearchKey(model.RefreshTokenSearchKeyUserAgentID), Value: agentID},
repository.Key{Key: usr_model.RefreshTokenSearchKey(model.RefreshTokenSearchKeyUserID), Value: userID},
)
return delete(db)
}
func DeleteUserRefreshTokens(db *gorm.DB, table, userID, instanceID string) error {
delete := repository.PrepareDeleteByKeys(table,
repository.Key{usr_model.RefreshTokenSearchKey(model.RefreshTokenSearchKeyUserID), userID},
repository.Key{usr_model.RefreshTokenSearchKey(model.RefreshTokenSearchKeyInstanceID), instanceID},
)
return delete(db)
}
func DeleteApplicationRefreshTokens(db *gorm.DB, table string, instanceID string, appIDs []string) error {
delete := repository.PrepareDeleteByKeys(table,
repository.Key{Key: usr_model.RefreshTokenSearchKey(model.RefreshTokenSearchKeyInstanceID), Value: instanceID},
repository.Key{Key: usr_model.RefreshTokenSearchKey(model.RefreshTokenSearchKeyApplicationID), Value: appIDs},
)
return delete(db)
}
func DeleteOrgRefreshTokens(db *gorm.DB, table string, instanceID, orgID string) error {
delete := repository.PrepareDeleteByKeys(table,
repository.Key{Key: usr_model.RefreshTokenSearchKey(model.RefreshTokenSearchKeyInstanceID), Value: instanceID},
repository.Key{Key: usr_model.RefreshTokenSearchKey(model.RefreshTokenSearchKeyResourceOwner), Value: orgID},
)
return delete(db)
}
func DeleteInstanceRefreshTokens(db *gorm.DB, table string, instanceID string) error {
delete := repository.PrepareDeleteByKey(table,
usr_model.RefreshTokenSearchKey(model.RefreshTokenSearchKeyInstanceID),
instanceID,
)
return delete(db)
}

View File

@ -47,74 +47,3 @@ func TokensByUserID(db *gorm.DB, table, userID, instanceID string) ([]*usr_model
_, err := query(db, &tokens)
return tokens, err
}
func PutToken(db *gorm.DB, table string, token *usr_model.TokenView) error {
save := repository.PrepareSave(table)
return save(db, token)
}
func PutTokens(db *gorm.DB, table string, tokens ...*usr_model.TokenView) error {
save := repository.PrepareBulkSave(table)
t := make([]interface{}, len(tokens))
for i, token := range tokens {
t[i] = token
}
return save(db, t...)
}
func DeleteToken(db *gorm.DB, table, tokenID, instanceID string) error {
delete := repository.PrepareDeleteByKeys(table,
repository.Key{Key: usr_model.TokenSearchKey(model.TokenSearchKeyTokenID), Value: tokenID},
repository.Key{Key: usr_model.TokenSearchKey(model.TokenSearchKeyInstanceID), Value: instanceID},
)
return delete(db)
}
func DeleteSessionTokens(db *gorm.DB, table, agentID, userID, instanceID string) error {
delete := repository.PrepareDeleteByKeys(table,
repository.Key{Key: usr_model.TokenSearchKey(model.TokenSearchKeyUserAgentID), Value: agentID},
repository.Key{Key: usr_model.TokenSearchKey(model.TokenSearchKeyUserID), Value: userID},
repository.Key{Key: usr_model.TokenSearchKey(model.TokenSearchKeyInstanceID), Value: instanceID},
)
return delete(db)
}
func DeleteUserTokens(db *gorm.DB, table, userID, instanceID string) error {
delete := repository.PrepareDeleteByKeys(table,
repository.Key{Key: usr_model.TokenSearchKey(model.TokenSearchKeyUserID), Value: userID},
repository.Key{Key: usr_model.TokenSearchKey(model.TokenSearchKeyInstanceID), Value: instanceID},
)
return delete(db)
}
func DeleteTokensFromRefreshToken(db *gorm.DB, table, refreshTokenID, instanceID string) error {
delete := repository.PrepareDeleteByKeys(table,
repository.Key{Key: usr_model.TokenSearchKey(model.TokenSearchKeyRefreshTokenID), Value: refreshTokenID},
repository.Key{Key: usr_model.TokenSearchKey(model.TokenSearchKeyInstanceID), Value: instanceID},
)
return delete(db)
}
func DeleteApplicationTokens(db *gorm.DB, table, instanceID string, appIDs []string) error {
delete := repository.PrepareDeleteByKeys(table,
repository.Key{Key: usr_model.TokenSearchKey(model.TokenSearchKeyApplicationID), Value: appIDs},
repository.Key{Key: usr_model.TokenSearchKey(model.TokenSearchKeyInstanceID), Value: instanceID},
)
return delete(db)
}
func DeleteInstanceTokens(db *gorm.DB, table, instanceID string) error {
delete := repository.PrepareDeleteByKey(table,
usr_model.TokenSearchKey(model.TokenSearchKeyInstanceID),
instanceID,
)
return delete(db)
}
func DeleteOrgTokens(db *gorm.DB, table, instanceID, orgID string) error {
delete := repository.PrepareDeleteByKeys(table,
repository.Key{Key: usr_model.TokenSearchKey(model.TokenSearchKeyResourceOwner), Value: orgID},
repository.Key{Key: usr_model.TokenSearchKey(model.TokenSearchKeyInstanceID), Value: instanceID},
)
return delete(db)
}

View File

@ -0,0 +1,93 @@
WITH auth_methods AS (
SELECT
user_id
, method_type
, token_id
, state
, instance_id
, name
FROM
projections.user_auth_methods4
WHERE
instance_id = $1
AND user_id = $2
),
verified_auth_methods AS (
SELECT
method_type
FROM
auth_methods
WHERE state = 2
)
SELECT
u.id
, u.creation_date
, LEAST(u.change_date, au.change_date) AS change_date
, u.resource_owner
, u.state AS user_state
, au.password_set
, h.password_change_required
, au.password_change
, au.last_login
, u.username AS user_name
, (SELECT array_agg(ll.login_name) login_names FROM projections.login_names3 ll
WHERE u.instance_id = ll.instance_id AND u.id = ll.user_id
GROUP BY ll.user_id, ll.instance_id) AS login_names
, l.login_name
, h.first_name
, h.last_name
, h.nick_name
, h.display_name
, h.preferred_language
, h.gender
, h.email
, h.is_email_verified
, h.phone
, h.is_phone_verified
, (SELECT COALESCE((SELECT state FROM auth_methods WHERE method_type = 1), 0)) AS otp_state
, CASE
WHEN EXISTS (SELECT true FROM verified_auth_methods WHERE method_type = 3) THEN 2
WHEN EXISTS (SELECT true FROM verified_auth_methods WHERE method_type = 2) THEN 1
ELSE 0
END AS mfa_max_set_up
, au.mfa_init_skipped
, u.sequence
, au.init_required
, au.username_change_required
, m.name AS machine_name
, m.description AS machine_description
, u.type AS user_type
, (SELECT
JSONB_AGG(json_build_object('webAuthNTokenId', token_id, 'webAuthNTokenName', name, 'state', state))
FROM auth_methods
WHERE method_type = 2
) AS u2f_tokens
, (SELECT
JSONB_AGG(json_build_object('webAuthNTokenId', token_id, 'webAuthNTokenName', name, 'state', state))
FROM auth_methods
WHERE method_type = 3
) AS passwordless_tokens
, h.avatar_key
, au.passwordless_init_required
, au.password_init_required
, u.instance_id
, (SELECT EXISTS (SELECT true FROM verified_auth_methods WHERE method_type = 6)) AS otp_sms_added
, (SELECT EXISTS (SELECT true FROM verified_auth_methods WHERE method_type = 7)) AS otp_email_added
FROM projections.users12 u
LEFT JOIN projections.users12_humans h
ON u.instance_id = h.instance_id
AND u.id = h.user_id
LEFT JOIN projections.login_names3 l
ON u.instance_id = l.instance_id
AND u.id = l.user_id
AND l.is_primary = true
LEFT JOIN projections.users12_machines m
ON u.instance_id = m.instance_id
AND u.id = m.user_id
LEFT JOIN auth.users3 au
ON u.instance_id = au.instance_id
AND u.id = au.id
WHERE
u.instance_id = $1
AND u.id = $2
LIMIT 1;

View File

@ -5,12 +5,8 @@ import (
_ "embed"
"errors"
"github.com/jinzhu/gorm"
"github.com/zitadel/zitadel/internal/database"
usr_model "github.com/zitadel/zitadel/internal/user/model"
"github.com/zitadel/zitadel/internal/user/repository/view/model"
"github.com/zitadel/zitadel/internal/view/repository"
"github.com/zitadel/zitadel/internal/zerrors"
)
@ -46,38 +42,8 @@ func UserSessionsByAgentID(db *database.DB, agentID, instanceID string) (userSes
return userSessions, err
}
func PutUserSession(db *gorm.DB, table string, session *model.UserSessionView) error {
save := repository.PrepareSave(table)
return save(db, session)
}
func DeleteUserSessions(db *gorm.DB, table, userID, instanceID string) error {
delete := repository.PrepareDeleteByKeys(table,
repository.Key{Key: model.UserSessionSearchKey(usr_model.UserSessionSearchKeyUserID), Value: userID},
repository.Key{Key: model.UserSessionSearchKey(usr_model.UserSessionSearchKeyInstanceID), Value: instanceID},
)
return delete(db)
}
func DeleteInstanceUserSessions(db *gorm.DB, table, instanceID string) error {
delete := repository.PrepareDeleteByKey(table,
model.UserSessionSearchKey(usr_model.UserSessionSearchKeyInstanceID),
instanceID,
)
return delete(db)
}
func DeleteOrgUserSessions(db *gorm.DB, table, instanceID, orgID string) error {
delete := repository.PrepareDeleteByKeys(table,
repository.Key{Key: model.UserSessionSearchKey(usr_model.UserSessionSearchKeyResourceOwner), Value: orgID},
repository.Key{Key: model.UserSessionSearchKey(usr_model.UserSessionSearchKeyInstanceID), Value: instanceID},
)
return delete(db)
}
func scanUserSession(row *sql.Row) (*model.UserSessionView, error) {
session := new(model.UserSessionView)
var userName, loginName, displayName, avatarKey sql.NullString
err := row.Scan(
&session.CreationDate,
&session.ChangeDate,
@ -85,10 +51,10 @@ func scanUserSession(row *sql.Row) (*model.UserSessionView, error) {
&session.State,
&session.UserAgentID,
&session.UserID,
&userName,
&loginName,
&displayName,
&avatarKey,
&session.UserName,
&session.LoginName,
&session.DisplayName,
&session.AvatarKey,
&session.SelectedIDPConfigID,
&session.PasswordVerification,
&session.PasswordlessVerification,
@ -103,10 +69,6 @@ func scanUserSession(row *sql.Row) (*model.UserSessionView, error) {
if errors.Is(err, sql.ErrNoRows) {
return nil, zerrors.ThrowNotFound(nil, "VIEW-NGBs1", "Errors.UserSession.NotFound")
}
session.UserName = userName.String
session.LoginName = loginName.String
session.DisplayName = displayName.String
session.AvatarKey = avatarKey.String
return session, err
}
@ -114,7 +76,6 @@ func scanUserSessions(rows *sql.Rows) ([]*model.UserSessionView, error) {
sessions := make([]*model.UserSessionView, 0)
for rows.Next() {
session := new(model.UserSessionView)
var userName, loginName, displayName, avatarKey sql.NullString
err := rows.Scan(
&session.CreationDate,
&session.ChangeDate,
@ -122,10 +83,10 @@ func scanUserSessions(rows *sql.Rows) ([]*model.UserSessionView, error) {
&session.State,
&session.UserAgentID,
&session.UserID,
&userName,
&loginName,
&displayName,
&avatarKey,
&session.UserName,
&session.LoginName,
&session.DisplayName,
&session.AvatarKey,
&session.SelectedIDPConfigID,
&session.PasswordVerification,
&session.PasswordlessVerification,
@ -140,10 +101,6 @@ func scanUserSessions(rows *sql.Rows) ([]*model.UserSessionView, error) {
if err != nil {
return nil, err
}
session.UserName = userName.String
session.LoginName = loginName.String
session.DisplayName = displayName.String
session.AvatarKey = avatarKey.String
sessions = append(sessions, session)
}

View File

@ -1,98 +1,42 @@
package view
import (
"github.com/jinzhu/gorm"
"context"
"database/sql"
_ "embed"
"errors"
"github.com/jinzhu/gorm"
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/domain"
usr_model "github.com/zitadel/zitadel/internal/user/model"
"github.com/zitadel/zitadel/internal/user/repository/view/model"
"github.com/zitadel/zitadel/internal/view/repository"
"github.com/zitadel/zitadel/internal/zerrors"
)
//go:embed user_by_id.sql
var userByIDQuery string
func UserByID(db *gorm.DB, table, userID, instanceID string) (*model.UserView, error) {
user := new(model.UserView)
userIDQuery := &model.UserSearchQuery{
Key: usr_model.UserSearchKeyUserID,
Method: domain.SearchMethodEquals,
Value: userID,
}
instanceIDQuery := &model.UserSearchQuery{
Key: usr_model.UserSearchKeyInstanceID,
Method: domain.SearchMethodEquals,
Value: instanceID,
}
ownerRemovedQuery := &model.UserSearchQuery{
Key: usr_model.UserSearchOwnerRemoved,
Method: domain.SearchMethodEquals,
Value: false,
}
query := repository.PrepareGetByQuery(table, userIDQuery, instanceIDQuery, ownerRemovedQuery)
err := query(db, user)
if zerrors.IsNotFound(err) {
return nil, zerrors.ThrowNotFound(nil, "VIEW-sj8Sw", "Errors.User.NotFound")
}
user.SetEmptyUserType()
return user, err
}
func UsersByOrgID(db *gorm.DB, table, orgID, instanceID string) ([]*model.UserView, error) {
users := make([]*model.UserView, 0)
orgIDQuery := &usr_model.UserSearchQuery{
Key: usr_model.UserSearchKeyResourceOwner,
Method: domain.SearchMethodEquals,
Value: orgID,
query := db.Raw(userByIDQuery, instanceID, userID)
tx := query.BeginTx(context.Background(), &sql.TxOptions{ReadOnly: true})
defer func() {
if err := tx.Commit().Error; err != nil {
logging.OnError(err).Info("commit failed")
}
tx.RollbackUnlessCommitted()
}()
err := tx.Scan(user).Error
if err == nil {
user.SetEmptyUserType()
return user, nil
}
instanceIDQuery := &usr_model.UserSearchQuery{
Key: usr_model.UserSearchKeyInstanceID,
Method: domain.SearchMethodEquals,
Value: instanceID,
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, zerrors.ThrowNotFound(err, "VIEW-hodc6", "object not found")
}
ownerRemovedQuery := &usr_model.UserSearchQuery{
Key: usr_model.UserSearchOwnerRemoved,
Method: domain.SearchMethodEquals,
Value: false,
}
query := repository.PrepareSearchQuery(table, model.UserSearchRequest{
Queries: []*usr_model.UserSearchQuery{orgIDQuery, instanceIDQuery, ownerRemovedQuery},
})
_, err := query(db, &users)
return users, err
}
func PutUsers(db *gorm.DB, table string, users ...*model.UserView) error {
save := repository.PrepareBulkSave(table)
u := make([]interface{}, len(users))
for i, user := range users {
u[i] = user
}
return save(db, u...)
}
func PutUser(db *gorm.DB, table string, user *model.UserView) error {
save := repository.PrepareSave(table)
return save(db, user)
}
func DeleteUser(db *gorm.DB, table, userID, instanceID string) error {
delete := repository.PrepareDeleteByKeys(table,
repository.Key{model.UserSearchKey(usr_model.UserSearchKeyUserID), userID},
repository.Key{model.UserSearchKey(usr_model.UserSearchKeyInstanceID), instanceID},
)
return delete(db)
}
func DeleteInstanceUsers(db *gorm.DB, table, instanceID string) error {
delete := repository.PrepareDeleteByKey(table, model.UserSearchKey(usr_model.UserSearchKeyInstanceID), instanceID)
return delete(db)
}
func UpdateOrgOwnerRemovedUsers(db *gorm.DB, table, instanceID, aggID string) error {
update := repository.PrepareUpdateByKeys(table,
model.UserSearchKey(usr_model.UserSearchOwnerRemoved),
true,
repository.Key{Key: model.UserSearchKey(usr_model.UserSearchKeyInstanceID), Value: instanceID},
repository.Key{Key: model.UserSearchKey(usr_model.UserSearchKeyResourceOwner), Value: aggID},
)
return update(db)
logging.WithFields("table ", table).WithError(err).Warn("get from cache error")
return nil, zerrors.ThrowInternal(err, "VIEW-qJBg9", "cache error")
}