package db import ( "context" "database/sql" "errors" "fmt" "github.com/kanootoko/codevault/internal/auth" ) var ErrInvalidCredentials = errors.New("invalid login or password") type UsersRepository struct { db *sql.DB } func NewUsersRepository(db *sql.DB) *UsersRepository { return &UsersRepository{db: db} } func (r *UsersRepository) Upsert(ctx context.Context, login, passwordHash string) error { _, err := r.db.ExecContext(ctx, ` INSERT INTO users (login, password_hash) VALUES (?, ?) ON CONFLICT(login) DO UPDATE SET password_hash = excluded.password_hash `, login, passwordHash) if err != nil { return fmt.Errorf("could not upsert user: %w", err) } return nil } // Verify checks the given login/password pair against the stored hash. A dummy hash // comparison is performed for unknown logins so that lookup timing does not reveal // whether the login exists. func (r *UsersRepository) Verify(ctx context.Context, login, password string) error { var passwordHash string err := r.db.QueryRowContext(ctx, "SELECT password_hash FROM users WHERE login = ?", login).Scan(&passwordHash) if errors.Is(err, sql.ErrNoRows) { auth.CompareDummy() return ErrInvalidCredentials } if err != nil { return fmt.Errorf("could not get user: %w", err) } if !auth.Compare(login, passwordHash, password) { return ErrInvalidCredentials } return nil }