mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
# Which Problems Are Solved As part of going to a relation storage model, we need to implement the repository for handling sessions and their events. # How the Problems Are Solved - Adds a migration step to create the necessary tables and types in the database. - Adds the repository implementation for sessions. - Implements a projection handler for session events to handle the events written by the old command handlers. - Updates the `Session` type and `SessionRepository` interface: - added `CreatorID` - Adjusted `Delete` method to return amount of affected columns - Removed sub-repository clumns from the main interface - Added `CreatorIDCondition`, `ExpirationCondition`, `ExistsFactor` and `ExistsMetadata` - Removed `SetUserAgent` - Methods of `SessionFactor` and `SessionChallenge` have been made public - fixed enumer on `SessionFactorType` - Adds a `JSON` type to the repository package to easy can JSON columns, resp. jsonb object inside a property of a `JSONArray` object. - Adds a `NoChange` interface, which can be implemented to signal that a `Change` interface implementation is not writing any changes to the statement builder, e.g. `CTEChange` # Additional Changes - Fixes the operators enums. `TextOperations` had wrong comments and `NumberOperations` additionally also had from comparisons. - Fixes downgrade of migration step 008. # Additional Context - closes https://github.com/zitadel/zitadel/issues/10212
262 lines
9.2 KiB
Go
262 lines
9.2 KiB
Go
package domain
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
|
"github.com/zitadel/zitadel/internal/crypto"
|
|
"github.com/zitadel/zitadel/internal/domain"
|
|
)
|
|
|
|
//go:generate enumer -type=SessionFactorType -trimprefix SessionFactorType -transform snake -linecomment -sql
|
|
type SessionFactorType uint8
|
|
|
|
const (
|
|
SessionFactorTypeUnknown SessionFactorType = iota
|
|
SessionFactorTypeUser
|
|
SessionFactorTypePassword
|
|
// SessionFactorTypePasskey was formerly known as WebAuthn
|
|
SessionFactorTypePasskey
|
|
SessionFactorTypeIdentityProviderIntent
|
|
// SessionFactorTypeTOTP used to be called OTP in some places
|
|
SessionFactorTypeTOTP
|
|
SessionFactorTypeOTPSMS // otp_sms
|
|
SessionFactorTypeOTPEmail
|
|
SessionFactorTypeRecoveryCode
|
|
)
|
|
|
|
type SessionFactor interface {
|
|
SessionFactorType() SessionFactorType
|
|
}
|
|
|
|
type SessionFactorUser struct {
|
|
UserID string `json:"userId,omitempty" db:"user_id"`
|
|
LastVerifiedAt time.Time `json:"lastVerifiedAt,omitzero" db:"last_verified_at"`
|
|
}
|
|
|
|
// SessionFactorType implements [SessionFactor].
|
|
func (s *SessionFactorUser) SessionFactorType() SessionFactorType {
|
|
return SessionFactorTypeUser
|
|
}
|
|
|
|
type SessionFactorPassword struct {
|
|
LastVerifiedAt time.Time `json:"lastVerifiedAt,omitzero" db:"last_verified_at"`
|
|
LastFailedAt time.Time `json:"lastFailedAt,omitzero" db:"last_failed_at"`
|
|
}
|
|
|
|
// SessionFactorType implements [SessionFactor].
|
|
func (s *SessionFactorPassword) SessionFactorType() SessionFactorType {
|
|
return SessionFactorTypePassword
|
|
}
|
|
|
|
type SessionFactorIdentityProviderIntent struct {
|
|
LastVerifiedAt time.Time `json:"lastVerifiedAt,omitzero" db:"last_verified_at"`
|
|
}
|
|
|
|
// SessionFactorType implements [SessionFactor].
|
|
func (s *SessionFactorIdentityProviderIntent) SessionFactorType() SessionFactorType {
|
|
return SessionFactorTypeIdentityProviderIntent
|
|
}
|
|
|
|
type SessionFactorPasskey struct {
|
|
LastVerifiedAt time.Time `json:"lastVerifiedAt,omitzero" db:"last_verified_at"`
|
|
UserVerified bool `json:"userVerified,omitempty" db:"user_verified"`
|
|
}
|
|
|
|
// SessionFactorType implements [SessionFactor].
|
|
func (s *SessionFactorPasskey) SessionFactorType() SessionFactorType {
|
|
return SessionFactorTypePasskey
|
|
}
|
|
|
|
type SessionFactorTOTP struct {
|
|
LastVerifiedAt time.Time `json:"lastVerifiedAt,omitzero" db:"last_verified_at"`
|
|
LastFailedAt time.Time `json:"lastFailedAt,omitzero" db:"last_failed_at"`
|
|
}
|
|
|
|
// SessionFactorType implements [SessionFactor].
|
|
func (s *SessionFactorTOTP) SessionFactorType() SessionFactorType {
|
|
return SessionFactorTypeTOTP
|
|
}
|
|
|
|
type SessionFactorOTPSMS struct {
|
|
LastVerifiedAt time.Time `json:"lastVerifiedAt,omitzero" db:"last_verified_at"`
|
|
LastFailedAt time.Time `json:"lastFailedAt,omitzero" db:"last_failed_at"`
|
|
}
|
|
|
|
// SessionFactorType implements [SessionFactor].
|
|
func (s *SessionFactorOTPSMS) SessionFactorType() SessionFactorType {
|
|
return SessionFactorTypeOTPSMS
|
|
}
|
|
|
|
type SessionFactorOTPEmail struct {
|
|
LastVerifiedAt time.Time `json:"lastVerifiedAt,omitzero" db:"last_verified_at"`
|
|
LastFailedAt time.Time `json:"lastFailedAt,omitzero" db:"last_failed_at"`
|
|
}
|
|
|
|
// SessionFactorType implements [SessionFactor].
|
|
func (s *SessionFactorOTPEmail) SessionFactorType() SessionFactorType {
|
|
return SessionFactorTypeOTPEmail
|
|
}
|
|
|
|
type SessionFactorRecoveryCode struct {
|
|
LastVerifiedAt time.Time `json:"lastVerifiedAt,omitzero" db:"last_verified_at"`
|
|
LastFailedAt time.Time `json:"lastFailedAt,omitzero" db:"last_failed_at"`
|
|
}
|
|
|
|
// SessionFactorType implements [SessionFactor].
|
|
func (s *SessionFactorRecoveryCode) SessionFactorType() SessionFactorType {
|
|
return SessionFactorTypeRecoveryCode
|
|
}
|
|
|
|
type SessionFactors []SessionFactor
|
|
|
|
func (s *SessionFactors) AppendTo(factor SessionFactor) {
|
|
*s = append(*s, factor)
|
|
}
|
|
|
|
// GetUserFactor returns the user factor from the factors.
|
|
func (s SessionFactors) GetUserFactor() *SessionFactorUser {
|
|
factor, _ := GetFactor[*SessionFactorUser](s)
|
|
return factor
|
|
}
|
|
|
|
// GetPasswordFactor returns the password factor from the factors.
|
|
func (s SessionFactors) GetPasswordFactor() *SessionFactorPassword {
|
|
factor, _ := GetFactor[*SessionFactorPassword](s)
|
|
return factor
|
|
}
|
|
|
|
// GetIDPIntentFactor returns the IDP Intent factor from the factors.
|
|
func (s SessionFactors) GetIDPIntentFactor() *SessionFactorIdentityProviderIntent {
|
|
factor, _ := GetFactor[*SessionFactorIdentityProviderIntent](s)
|
|
return factor
|
|
}
|
|
|
|
// GetPasskeyFactor returns the passkey factor from the factors.
|
|
func (s SessionFactors) GetPasskeyFactor() *SessionFactorPasskey {
|
|
factor, _ := GetFactor[*SessionFactorPasskey](s)
|
|
return factor
|
|
}
|
|
|
|
// GetTOTPFactor returns the TOTP factor from the factors.
|
|
func (s SessionFactors) GetTOTPFactor() *SessionFactorTOTP {
|
|
factor, _ := GetFactor[*SessionFactorTOTP](s)
|
|
return factor
|
|
}
|
|
|
|
// GetOTPSMSFactor returns the OTP SMS factor from the factors.
|
|
func (s SessionFactors) GetOTPSMSFactor() *SessionFactorOTPSMS {
|
|
factor, _ := GetFactor[*SessionFactorOTPSMS](s)
|
|
return factor
|
|
}
|
|
|
|
// GetOTPEmailFactor returns the OTP Email factor from the factors.
|
|
func (s SessionFactors) GetOTPEmailFactor() *SessionFactorOTPEmail {
|
|
factor, _ := GetFactor[*SessionFactorOTPEmail](s)
|
|
return factor
|
|
}
|
|
|
|
// GetRecoveryCodeFactor returns the recovery code factor from the factors.
|
|
func (s SessionFactors) GetRecoveryCodeFactor() *SessionFactorRecoveryCode {
|
|
factor, _ := GetFactor[*SessionFactorRecoveryCode](s)
|
|
return factor
|
|
}
|
|
|
|
// GetFactor returns the factor of the given type from the factors.
|
|
func GetFactor[T SessionFactor](factors SessionFactors) (T, bool) {
|
|
var nilT T
|
|
for _, factor := range factors {
|
|
if f, ok := factor.(T); ok {
|
|
return f, true
|
|
}
|
|
}
|
|
return nilT, false
|
|
}
|
|
|
|
type SessionChallenge interface {
|
|
SessionChallengeType() SessionFactorType
|
|
}
|
|
|
|
type SessionChallengePasskey struct {
|
|
LastChallengedAt time.Time `json:"lastChallengedAt,omitzero" db:"last_challenged_at"`
|
|
Challenge string `json:"challenge,omitempty" db:"challenge"`
|
|
AllowedCredentialIDs [][]byte `json:"allowedCredentialIDs,omitempty" db:"allowed_credential_ids"`
|
|
UserVerification domain.UserVerificationRequirement `json:"userVerification,omitempty" db:"user_verification"`
|
|
RPID string `json:"rpId,omitempty" db:"rp_id"`
|
|
}
|
|
|
|
// SessionChallengeType implements [SessionChallenge].
|
|
func (s *SessionChallengePasskey) SessionChallengeType() SessionFactorType {
|
|
return SessionFactorTypePasskey
|
|
}
|
|
|
|
type SessionChallengeOTPSMS struct {
|
|
LastChallengedAt time.Time `json:"lastChallengedAt,omitzero" db:"last_challenged_at"`
|
|
Code *crypto.CryptoValue `json:"code,omitempty" db:"code"`
|
|
Expiry time.Duration `json:"expiry,omitzero" db:"expiry"`
|
|
CodeReturned bool `json:"codeReturned,omitempty" db:"code_returned"`
|
|
GeneratorID string `json:"generatorId,omitempty" db:"generator_id"`
|
|
TriggeredAtOrigin string `json:"triggeredAtOrigin,omitempty" db:"triggered_at_origin"`
|
|
}
|
|
|
|
// SessionChallengeType implements [SessionChallenge].
|
|
func (s *SessionChallengeOTPSMS) SessionChallengeType() SessionFactorType {
|
|
return SessionFactorTypeOTPSMS
|
|
}
|
|
|
|
type SessionChallengeOTPEmail struct {
|
|
LastChallengedAt time.Time `json:"lastChallengedAt,omitzero" db:"last_challenged_at"`
|
|
Code *crypto.CryptoValue `json:"code,omitempty" db:"code"`
|
|
Expiry time.Duration `json:"expiry,omitzero" db:"expiry"`
|
|
CodeReturned bool `json:"codeReturned,omitempty" db:"code_returned"`
|
|
URLTemplate string `json:"urlTemplate,omitempty" db:"url_template"`
|
|
TriggeredAtOrigin string `json:"triggeredAtOrigin,omitempty" db:"triggered_at_origin"`
|
|
}
|
|
|
|
// SessionChallengeType implements [SessionChallenge].
|
|
func (s *SessionChallengeOTPEmail) SessionChallengeType() SessionFactorType {
|
|
return SessionFactorTypeOTPEmail
|
|
}
|
|
|
|
type SessionChallenges []SessionChallenge
|
|
|
|
func (s *SessionChallenges) AppendTo(challenge SessionChallenge) {
|
|
*s = append(*s, challenge)
|
|
}
|
|
|
|
// GetPasskeyChallenge returns the passkey challenge from the challenges.
|
|
func (s SessionChallenges) GetPasskeyChallenge() *SessionChallengePasskey {
|
|
challenge, _ := GetChallenge[*SessionChallengePasskey](s)
|
|
return challenge
|
|
}
|
|
|
|
// GetOTPSMSChallenge returns the OTP SMS challenge from the challenges.
|
|
func (s SessionChallenges) GetOTPSMSChallenge() *SessionChallengeOTPSMS {
|
|
challenge, _ := GetChallenge[*SessionChallengeOTPSMS](s)
|
|
return challenge
|
|
}
|
|
|
|
// GetOTPEmailChallenge returns the OTP Email challenge from the challenges.
|
|
func (s SessionChallenges) GetOTPEmailChallenge() *SessionChallengeOTPEmail {
|
|
challenge, _ := GetChallenge[*SessionChallengeOTPEmail](s)
|
|
return challenge
|
|
}
|
|
|
|
// GetChallenge returns the challenge of the given type from the challenges.
|
|
func GetChallenge[T SessionChallenge](challenges SessionChallenges) (T, bool) {
|
|
var nilT T
|
|
for _, challenge := range challenges {
|
|
if c, ok := challenge.(T); ok {
|
|
return c, true
|
|
}
|
|
}
|
|
return nilT, false
|
|
}
|
|
|
|
type SessionFactorConditions interface {
|
|
// FactorTypeCondition returns an equal filter on the factor type field.
|
|
FactorTypeCondition(factorType SessionFactorType) database.Condition
|
|
// LastVerifiedBeforeCondition returns a filter on the factor last verified field before the given time.
|
|
LastVerifiedBeforeCondition(lastVerifiedAt time.Time) database.Condition
|
|
}
|