package command import ( "context" "time" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/repository/samlrequest" "github.com/zitadel/zitadel/internal/telemetry/tracing" "github.com/zitadel/zitadel/internal/zerrors" ) type SAMLRequest struct { ID string LoginClient string ApplicationID string ACSURL string RelayState string RequestID string Binding string Issuer string Destination string } type CurrentSAMLRequest struct { *SAMLRequest SessionID string UserID string AuthMethods []domain.UserAuthMethodType AuthTime time.Time } func (c *Commands) AddSAMLRequest(ctx context.Context, samlRequest *SAMLRequest) (_ *CurrentSAMLRequest, err error) { id, err := c.idGenerator.Next() if err != nil { return nil, err } samlRequest.ID = IDPrefixV2 + id writeModel, err := c.getSAMLRequestWriteModel(ctx, samlRequest.ID) if err != nil { return nil, err } if writeModel.SAMLRequestState != domain.SAMLRequestStateUnspecified { return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-MO3vmsMLUt", "Errors.SAMLRequest.AlreadyExisting") } err = c.pushAppendAndReduce(ctx, writeModel, samlrequest.NewAddedEvent( ctx, &samlrequest.NewAggregate(samlRequest.ID, authz.GetInstance(ctx).InstanceID()).Aggregate, samlRequest.LoginClient, samlRequest.ApplicationID, samlRequest.ACSURL, samlRequest.RelayState, samlRequest.RequestID, samlRequest.Binding, samlRequest.Issuer, samlRequest.Destination, )) if err != nil { return nil, err } return samlRequestWriteModelToCurrentSAMLRequest(writeModel), nil } func (c *Commands) LinkSessionToSAMLRequest(ctx context.Context, id, sessionID, sessionToken string, checkLoginClient bool) (*domain.ObjectDetails, *CurrentSAMLRequest, error) { writeModel, err := c.getSAMLRequestWriteModel(ctx, id) if err != nil { return nil, nil, err } if writeModel.SAMLRequestState == domain.SAMLRequestStateUnspecified { return nil, nil, zerrors.ThrowNotFound(nil, "COMMAND-GH3PVLSfXC", "Errors.SAMLRequest.NotExisting") } if writeModel.SAMLRequestState != domain.SAMLRequestStateAdded { return nil, nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-ttPKNdAIFT", "Errors.SAMLRequest.AlreadyHandled") } if checkLoginClient && authz.GetCtxData(ctx).UserID != writeModel.LoginClient { return nil, nil, zerrors.ThrowPermissionDenied(nil, "COMMAND-KCd48Rxt7x", "Errors.SAMLRequest.WrongLoginClient") } sessionWriteModel := NewSessionWriteModel(sessionID, authz.GetInstance(ctx).InstanceID()) err = c.eventstore.FilterToQueryReducer(ctx, sessionWriteModel) if err != nil { return nil, nil, err } if err = sessionWriteModel.CheckIsActive(); err != nil { return nil, nil, err } if err := c.sessionTokenVerifier(ctx, sessionToken, sessionWriteModel.AggregateID, sessionWriteModel.TokenID); err != nil { return nil, nil, err } if err := c.pushAppendAndReduce(ctx, writeModel, samlrequest.NewSessionLinkedEvent( ctx, &samlrequest.NewAggregate(id, authz.GetInstance(ctx).InstanceID()).Aggregate, sessionID, sessionWriteModel.UserID, sessionWriteModel.AuthenticationTime(), sessionWriteModel.AuthMethodTypes(), )); err != nil { return nil, nil, err } return writeModelToObjectDetails(&writeModel.WriteModel), samlRequestWriteModelToCurrentSAMLRequest(writeModel), nil } func (c *Commands) FailSAMLRequest(ctx context.Context, id string, reason domain.SAMLErrorReason) (*domain.ObjectDetails, *CurrentSAMLRequest, error) { writeModel, err := c.getSAMLRequestWriteModel(ctx, id) if err != nil { return nil, nil, err } if writeModel.SAMLRequestState != domain.SAMLRequestStateAdded { return nil, nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-32lGj1Fhjt", "Errors.SAMLRequest.AlreadyHandled") } err = c.pushAppendAndReduce(ctx, writeModel, samlrequest.NewFailedEvent( ctx, &samlrequest.NewAggregate(id, authz.GetInstance(ctx).InstanceID()).Aggregate, reason, )) if err != nil { return nil, nil, err } return writeModelToObjectDetails(&writeModel.WriteModel), samlRequestWriteModelToCurrentSAMLRequest(writeModel), nil } func samlRequestWriteModelToCurrentSAMLRequest(writeModel *SAMLRequestWriteModel) (_ *CurrentSAMLRequest) { return &CurrentSAMLRequest{ SAMLRequest: &SAMLRequest{ ID: writeModel.AggregateID, LoginClient: writeModel.LoginClient, ApplicationID: writeModel.ApplicationID, ACSURL: writeModel.ACSURL, RelayState: writeModel.RelayState, RequestID: writeModel.RequestID, Binding: writeModel.Binding, Issuer: writeModel.Issuer, Destination: writeModel.Destination, }, SessionID: writeModel.SessionID, UserID: writeModel.UserID, AuthMethods: writeModel.AuthMethods, AuthTime: writeModel.AuthTime, } } func (c *Commands) GetCurrentSAMLRequest(ctx context.Context, id string) (_ *CurrentSAMLRequest, err error) { wm, err := c.getSAMLRequestWriteModel(ctx, id) if err != nil { return nil, err } return samlRequestWriteModelToCurrentSAMLRequest(wm), nil } func (c *Commands) getSAMLRequestWriteModel(ctx context.Context, id string) (writeModel *SAMLRequestWriteModel, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() writeModel = NewSAMLRequestWriteModel(ctx, id) err = c.eventstore.FilterToQueryReducer(ctx, writeModel) if err != nil { return nil, err } return writeModel, nil }