mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-25 00:32:15 +00:00
feat(relational): define session repository (#11054)
# Which Problems Are Solved As part moving sessions to the relational table, the repository to be able to manage session needs to be defined. This allows the business logic / service-layer to implement the necessary commands and the storage-layer to implement the underlying repository. # How the Problems Are Solved This PR defines the `SessionRepository` with all underlying object like the user agent, metadata, factors (including. challenges). # Additional Changes None # Additional Context - relates to #10212 --------- Co-authored-by: Copilot <[email protected]>
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
InstanceID string `json:"instance_id,omitempty" db:"instance_id"`
|
||||
ID string `json:"id,omitempty" db:"id"`
|
||||
Token string `json:"token,omitempty" db:"token"`
|
||||
Lifetime time.Duration `json:"lifetime,omitempty" db:"lifetime"`
|
||||
Expiration time.Time `json:"expiration,omitzero" db:"expiration"`
|
||||
UserID string `json:"user_id,omitempty" db:"user_id"`
|
||||
CreatedAt time.Time `json:"created_at,omitzero" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitzero" db:"updated_at"`
|
||||
Factors SessionFactors `json:"factors,omitempty"`
|
||||
Challenges SessionChallenges `json:"challenges,omitempty"`
|
||||
Metadata []SessionMetadata `json:"metadata,omitempty"`
|
||||
UserAgent *SessionUserAgent `json:"user_agent,omitempty"`
|
||||
}
|
||||
|
||||
//go:generate mockgen -typed -package domainmock -destination ./mock/session.mock.go . SessionRepository
|
||||
|
||||
type SessionRepository interface {
|
||||
Repository
|
||||
|
||||
sessionColumns
|
||||
sessionConditions
|
||||
sessionChanges
|
||||
|
||||
// Get returns a session based on the given condition.
|
||||
Get(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) (*Session, error)
|
||||
// List returns a list of sessions based on the given condition.
|
||||
List(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) ([]*Session, error)
|
||||
// Create creates a new session.
|
||||
Create(ctx context.Context, client database.QueryExecutor, user *Session) error
|
||||
// Update one or more existing sessions.
|
||||
// The condition must include at least the instanceID of the session to update.
|
||||
Update(ctx context.Context, client database.QueryExecutor, condition database.Condition, changes ...database.Change) (int64, error)
|
||||
// Delete removes sessions based on the given condition.
|
||||
Delete(ctx context.Context, client database.QueryExecutor, condition database.Condition) error
|
||||
}
|
||||
|
||||
// sessionColumns define all the columns of the session table.
|
||||
type sessionColumns interface {
|
||||
// InstanceIDColumn returns the column for the instance id field.
|
||||
InstanceIDColumn() database.Column
|
||||
// IDColumn returns the column for the id field.
|
||||
IDColumn() database.Column
|
||||
// TokenColumn returns the column for the token field.
|
||||
TokenColumn() database.Column
|
||||
// LifetimeColumn returns the column for the lifetime field.
|
||||
LifetimeColumn() database.Column
|
||||
// ExpirationColumn returns the column for the expiration field.
|
||||
ExpirationColumn() database.Column
|
||||
// UserIDColumn returns the column for the user id field.
|
||||
UserIDColumn() database.Column
|
||||
// CreatedAtColumn returns the column for the created at field.
|
||||
CreatedAtColumn() database.Column
|
||||
// UpdatedAtColumn returns the column for the updated at field.
|
||||
UpdatedAtColumn() database.Column
|
||||
|
||||
// FactorColumns returns the columns for the factors fields.
|
||||
FactorColumns() sessionFactorColumns
|
||||
// MetadataColumns returns the columns for the metadata fields.
|
||||
MetadataColumns() sessionMetadataColumns
|
||||
// UserAgentColumns returns the columns for the user agent fields.
|
||||
UserAgentColumns() sessionUserAgentColumns
|
||||
}
|
||||
|
||||
// sessionConditions define all the conditions for the session table.
|
||||
type sessionConditions interface {
|
||||
// PrimaryKeyCondition returns a filter on the primary key fields.
|
||||
PrimaryKeyCondition(instanceID, sessionID string) database.Condition
|
||||
// InstanceIDCondition returns an equal filter on the instance id field.
|
||||
InstanceIDCondition(instanceID string) database.Condition
|
||||
// IDCondition returns an equal filter on the id field.
|
||||
IDCondition(sessionID string) database.Condition
|
||||
// UserAgentIDCondition returns an equal filter on the user agent ID field.
|
||||
UserAgentIDCondition(userAgentID string) database.Condition
|
||||
// UserIDCondition returns an equal filter on the user id field.
|
||||
UserIDCondition(userID string) database.Condition
|
||||
// CreatedAtCondition returns a filter on the created at field.
|
||||
CreatedAtCondition(op database.NumberOperation, createdAt time.Time) database.Condition
|
||||
// UpdatedAtCondition returns a filter on the updated at field.
|
||||
UpdatedAtCondition(op database.NumberOperation, updatedAt time.Time) database.Condition
|
||||
|
||||
// FactorConditions returns the conditions for the factors fields.
|
||||
FactorConditions() sessionFactorConditions
|
||||
// MetadataConditions returns the conditions for the metadata fields.
|
||||
MetadataConditions() sessionMetadataConditions
|
||||
// UserAgentConditions returns the conditions for the user agent fields.
|
||||
UserAgentConditions() sessionUserAgentConditions
|
||||
}
|
||||
|
||||
type sessionChanges interface {
|
||||
// SetUpdatedAt sets the updated at column.
|
||||
// Only use this when reducing events,
|
||||
// during regular updates the DB sets this column automatically.
|
||||
SetUpdatedAt(updatedAt time.Time) database.Change
|
||||
// SetToken sets the token field of the session.
|
||||
SetToken(token string) database.Change
|
||||
// SetLifetime sets the lifetime field of the session and will update the computed expiration field.
|
||||
SetLifetime(lifetime time.Duration) database.Change
|
||||
|
||||
// SetChallenge adds or updates the challenge of the corresponding type.
|
||||
SetChallenge(challenge SessionChallenge) database.Change
|
||||
// SetFactor adds or updates the factor of the corresponding type.
|
||||
SetFactor(factor SessionFactor) database.Change
|
||||
// ClearFactor resets the factor of the corresponding type.
|
||||
ClearFactor(factor SessionFactorType) database.Change
|
||||
// SetMetadata adds or updates the metadata of the session.
|
||||
SetMetadata(metadata []SessionMetadata) database.Change
|
||||
// SetUserAgent adds or updates the user agent of the session.
|
||||
SetUserAgent(userAgent SessionUserAgent) database.Change
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
type SessionFactorType int
|
||||
|
||||
const (
|
||||
SessionFactorTypeUnknown SessionFactorType = iota
|
||||
SessionFactorTypeUser
|
||||
SessionFactorTypePassword
|
||||
SessionFactorTypePasskey // formerly known as WebAuthn
|
||||
SessionFactorTypeIdentityProviderIntent
|
||||
SessionFactorTypeTOTP
|
||||
SessionFactorTypeOTPSMS
|
||||
SessionFactorTypeOTPEmail
|
||||
)
|
||||
|
||||
type SessionFactor interface {
|
||||
sessionFactorType() SessionFactorType
|
||||
}
|
||||
|
||||
type SessionFactorUser struct {
|
||||
UserID string
|
||||
LastVerifiedAt time.Time
|
||||
}
|
||||
|
||||
// sessionFactorType implements [SessionFactor].
|
||||
func (s *SessionFactorUser) sessionFactorType() SessionFactorType {
|
||||
return SessionFactorTypeUser
|
||||
}
|
||||
|
||||
type SessionFactorPassword struct {
|
||||
LastVerifiedAt time.Time
|
||||
LastFailedAt time.Time
|
||||
}
|
||||
|
||||
// sessionFactorType implements [SessionFactor].
|
||||
func (s *SessionFactorPassword) sessionFactorType() SessionFactorType {
|
||||
return SessionFactorTypePassword
|
||||
}
|
||||
|
||||
type SessionFactorIdentityProviderIntent struct {
|
||||
LastVerifiedAt time.Time
|
||||
}
|
||||
|
||||
// sessionFactorType implements [SessionFactor].
|
||||
func (s *SessionFactorIdentityProviderIntent) sessionFactorType() SessionFactorType {
|
||||
return SessionFactorTypeIdentityProviderIntent
|
||||
}
|
||||
|
||||
type SessionFactorPasskey struct {
|
||||
LastVerifiedAt time.Time
|
||||
UserVerified bool
|
||||
}
|
||||
|
||||
// sessionFactorType implements [SessionFactor].
|
||||
func (s *SessionFactorPasskey) sessionFactorType() SessionFactorType {
|
||||
return SessionFactorTypePasskey
|
||||
}
|
||||
|
||||
type SessionFactorTOTP struct {
|
||||
LastVerifiedAt time.Time
|
||||
LastFailedAt time.Time
|
||||
}
|
||||
|
||||
// sessionFactorType implements [SessionFactor].
|
||||
func (s *SessionFactorTOTP) sessionFactorType() SessionFactorType {
|
||||
return SessionFactorTypeTOTP
|
||||
}
|
||||
|
||||
type SessionFactorOTPSMS struct {
|
||||
LastVerifiedAt time.Time
|
||||
LastFailedAt time.Time
|
||||
}
|
||||
|
||||
// sessionFactorType implements [SessionFactor].
|
||||
func (s *SessionFactorOTPSMS) sessionFactorType() SessionFactorType {
|
||||
return SessionFactorTypeOTPSMS
|
||||
}
|
||||
|
||||
type SessionFactorOTPEmail struct {
|
||||
LastVerifiedAt time.Time
|
||||
LastFailedAt time.Time
|
||||
}
|
||||
|
||||
// sessionFactorType implements [SessionFactor].
|
||||
func (s *SessionFactorOTPEmail) sessionFactorType() SessionFactorType {
|
||||
return SessionFactorTypeOTPEmail
|
||||
}
|
||||
|
||||
type SessionFactors []SessionFactor
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
Challenge string
|
||||
AllowedCredentialIDs [][]byte
|
||||
UserVerification domain.UserVerificationRequirement
|
||||
RPID string
|
||||
}
|
||||
|
||||
// sessionChallengeType implements [SessionChallenge].
|
||||
func (s *SessionChallengePasskey) sessionChallengeType() SessionFactorType {
|
||||
return SessionFactorTypePasskey
|
||||
}
|
||||
|
||||
type SessionChallengeOTPSMS struct {
|
||||
LastChallengedAt time.Time
|
||||
Code *crypto.CryptoValue
|
||||
Expiry time.Duration
|
||||
CodeReturned bool
|
||||
GeneratorID string
|
||||
TriggeredAtOrigin string
|
||||
}
|
||||
|
||||
// sessionChallengeType implements [SessionChallenge].
|
||||
func (s *SessionChallengeOTPSMS) sessionChallengeType() SessionFactorType {
|
||||
return SessionFactorTypeOTPSMS
|
||||
}
|
||||
|
||||
type SessionChallengeOTPEmail struct {
|
||||
LastChallengedAt time.Time
|
||||
Code *crypto.CryptoValue
|
||||
Expiry time.Duration
|
||||
CodeReturned bool
|
||||
URLTmpl string
|
||||
TriggeredAtOrigin string
|
||||
}
|
||||
|
||||
// sessionChallengeType implements [SessionChallenge].
|
||||
func (s *SessionChallengeOTPEmail) sessionChallengeType() SessionFactorType {
|
||||
return SessionFactorTypeOTPEmail
|
||||
}
|
||||
|
||||
type SessionChallenges []SessionChallenge
|
||||
|
||||
// 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 sessionFactorColumns interface {
|
||||
// InstanceIDColumn returns the column for the instance id field.
|
||||
InstanceIDColumn() database.Column
|
||||
// SessionIDColumn returns the column for the session id field.
|
||||
SessionIDColumn() database.Column
|
||||
// FactorTypeColumn returns the column for the factor type field.
|
||||
FactorTypeColumn() database.Column
|
||||
// LastChallengedAtColumn returns the column for the last challenged at field.
|
||||
LastChallengedAtColumn() database.Column
|
||||
// LastFailedAtColumn returns the column for the last failed at field.
|
||||
LastFailedAtColumn() database.Column
|
||||
// LastVerifiedAtColumn returns the column for the last verified at field.
|
||||
LastVerifiedAtColumn() database.Column
|
||||
}
|
||||
|
||||
type sessionFactorConditions interface {
|
||||
// PrimaryKeyCondition returns a filter on the primary key fields.
|
||||
PrimaryKeyCondition(instanceID, sessionID string, factorType SessionFactorType) database.Condition
|
||||
// InstanceIDCondition returns an equal filter on the instance id field.
|
||||
InstanceIDCondition(instanceID string) database.Condition
|
||||
// SessionIDCondition returns an equal filter on the session id field.
|
||||
SessionIDCondition(sessionID string) database.Condition
|
||||
// FactorTypeCondition returns an equal filter on the factor type field.
|
||||
FactorTypeCondition(factorType SessionFactorType) database.Condition
|
||||
// FactorLastVerifiedBeforeCondition returns a filter on the factor last verified field before the given time.
|
||||
FactorLastVerifiedBeforeCondition(lastVerifiedAt time.Time) database.Condition
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package domain
|
||||
|
||||
import "github.com/zitadel/zitadel/backend/v3/storage/database"
|
||||
|
||||
type SessionMetadata struct {
|
||||
Metadata
|
||||
SessionID string `json:"sessionId,omitempty" db:"session_id"`
|
||||
}
|
||||
|
||||
type sessionMetadataColumns interface {
|
||||
MetadataColumns
|
||||
// SessionIDColumn returns the column for the session id field.
|
||||
SessionIDColumn() database.Column
|
||||
}
|
||||
|
||||
type sessionMetadataConditions interface {
|
||||
MetadataConditions
|
||||
// PrimaryKeyCondition returns a filter on the primary key fields.
|
||||
PrimaryKeyCondition(instanceID, sessionID, key string) database.Condition
|
||||
// SessionIDCondition returns an equal filter on the session id field.
|
||||
SessionIDCondition(sessionID string) database.Condition
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
||||
)
|
||||
|
||||
type SessionUserAgent struct {
|
||||
InstanceID string `json:"instanceId,omitempty" db:"instance_id"`
|
||||
FingerprintID *string `json:"fingerprintId,omitempty" db:"fingerprint_id"`
|
||||
Description *string `json:"description,omitempty" db:"description"`
|
||||
IP net.IP `json:"ip,omitempty" db:"ip"`
|
||||
Header http.Header `json:"header,omitempty" db:"headers"`
|
||||
}
|
||||
|
||||
type sessionUserAgentColumns interface {
|
||||
// InstanceIDColumn returns the column for the instance id field.
|
||||
InstanceIDColumn() database.Column
|
||||
// FingerprintIDColumn returns the column for the fingerprint id field.
|
||||
FingerprintIDColumn() database.Column
|
||||
// IPColumn returns the column for the ip field.
|
||||
IPColumn() database.Column
|
||||
// DescriptionColumn returns the column for the description field.
|
||||
DescriptionColumn() database.Column
|
||||
// HeadersColumn returns the column for the headers field.
|
||||
HeadersColumn() database.Column
|
||||
}
|
||||
|
||||
type sessionUserAgentConditions interface {
|
||||
// PrimaryKeyCondition returns a filter on the primary key fields.
|
||||
PrimaryKeyCondition(instanceID, fingerprintID string) database.Condition
|
||||
// InstanceIDCondition returns an equal filter on the instance id field.
|
||||
InstanceIDCondition(instanceID string) database.Condition
|
||||
// FingerprintIDCondition returns an equal filter on the fingerprint id field.
|
||||
FingerprintIDCondition(fingerprintID string) database.Condition
|
||||
}
|
||||
Reference in New Issue
Block a user