Files
enkly-ai/services/iam/internal/domain/user.go
enkly 79ce8dc4c0
Some checks failed
Go CI Pipeline / build_and_test (push) Failing after 22m0s
Init Project
2026-10-30 18:06:55 +01:00

93 lines
3.4 KiB
Go

// Package domain holds the IAM bounded context's entities and the
// repository interfaces its application layer depends on. Nothing here
// imports database/sql, net/http or gRPC — see ARCHITECTURE.md's note
// on dependency direction ("domain depends on nothing; everything else
// depends on domain").
package domain
import (
"context"
"time"
)
// User is an enkly account — identical in shape to the monolith's
// models.User, minus the json tags (this is an internal entity now, not
// a value serialized straight to a template).
type User struct {
ID int64
Email string
PasswordHash string
EmailVerified bool
CreatedAt time.Time
}
// Session is one logged-in browser, identified at rest by the SHA-256
// hash of its cookie token (see pkg/authsupport.HashToken) — the raw
// token exists only in the cookie itself and the gRPC response that
// minted it.
type Session struct {
UserID int64
TokenHash string
ExpiresAt time.Time
}
// VerificationCode is a 6-digit email-verification code, stored only as
// its hash plus an attempt counter (see internal/db's original comment
// on why a short code needs active guessing defense that a 256-bit
// token wouldn't).
type VerificationCode struct {
UserID int64
CodeHash string
Attempts int
ExpiresAt time.Time
UsedAt *time.Time
}
// PasswordResetToken is a "forgot password" link's token, stored only
// as its hash.
type PasswordResetToken struct {
UserID int64
TokenHash string
ExpiresAt time.Time
UsedAt *time.Time
}
// UserRepository persists and retrieves User rows. Implemented by
// internal/infrastructure/postgres.UserRepo.
type UserRepository interface {
Create(ctx context.Context, email, passwordHash string) (*User, error)
GetByEmail(ctx context.Context, email string) (*User, error)
GetByID(ctx context.Context, id int64) (*User, error)
MarkEmailVerified(ctx context.Context, userID int64) error
UpdatePassword(ctx context.Context, userID int64, newPasswordHash string) error
}
// SessionRepository persists and retrieves Session rows.
type SessionRepository interface {
Create(ctx context.Context, userID int64, tokenHash string, expiresAt time.Time) error
GetUserByTokenHash(ctx context.Context, tokenHash string) (*User, error)
Delete(ctx context.Context, tokenHash string) error
DeleteAllForUser(ctx context.Context, userID int64) error
}
// VerificationRepository persists and retrieves VerificationCode rows.
type VerificationRepository interface {
Create(ctx context.Context, userID int64, codeHash string, expiresAt time.Time) error
// VerifyAndConsume replaces db.VerifyEmailCode: checks the most
// recent unused, unexpired code for userID against codeHash,
// incrementing its attempt counter on a mismatch and marking it
// used (and the user verified) on a match. Returns domain.ErrInvalid
// when no code matches.
VerifyAndConsume(ctx context.Context, userID int64, codeHash string) error
}
// PasswordResetRepository persists and retrieves PasswordResetToken rows.
type PasswordResetRepository interface {
Create(ctx context.Context, userID int64, tokenHash string, expiresAt time.Time) error
Valid(ctx context.Context, tokenHash string) (bool, error)
// ConsumeAndResetPassword replaces db.ResetPassword: validates the
// token, updates the password hash, marks the token used, and
// deletes every session for the user, all in one transaction.
ConsumeAndResetPassword(ctx context.Context, tokenHash, newPasswordHash string) (userID int64, err error)
}