103 lines
2.5 KiB
Go
103 lines
2.5 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
)
|
|
|
|
type User struct {
|
|
ID string
|
|
Username string
|
|
PasswordHash string
|
|
Role string // Legacy field, kept for backward compatibility
|
|
CreatedAt string
|
|
}
|
|
|
|
type UserStore struct {
|
|
DB *sql.DB
|
|
}
|
|
|
|
func NewUserStore(db *sql.DB) *UserStore {
|
|
return &UserStore{DB: db}
|
|
}
|
|
|
|
// GetUserByUsername retrieves a user by username
|
|
func (s *UserStore) GetUserByUsername(ctx context.Context, username string) (*User, error) {
|
|
var user User
|
|
err := s.DB.QueryRowContext(ctx,
|
|
`SELECT id, username, password_hash, role, created_at FROM users WHERE username = ?`,
|
|
username).Scan(&user.ID, &user.Username, &user.PasswordHash, &user.Role, &user.CreatedAt)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, errors.New("user not found")
|
|
}
|
|
return nil, err
|
|
}
|
|
return &user, nil
|
|
}
|
|
|
|
// GetUserByID retrieves a user by ID
|
|
func (s *UserStore) GetUserByID(ctx context.Context, userID string) (*User, error) {
|
|
var user User
|
|
err := s.DB.QueryRowContext(ctx,
|
|
`SELECT id, username, password_hash, role, created_at FROM users WHERE id = ?`,
|
|
userID).Scan(&user.ID, &user.Username, &user.PasswordHash, &user.Role, &user.CreatedAt)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, errors.New("user not found")
|
|
}
|
|
return nil, err
|
|
}
|
|
return &user, nil
|
|
}
|
|
|
|
// CreateUser creates a new user
|
|
func (s *UserStore) CreateUser(ctx context.Context, username, password string) (*User, error) {
|
|
passwordHash, err := HashPassword(password)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
userID := username // Using username as ID for simplicity, could use UUID
|
|
_, err = s.DB.ExecContext(ctx,
|
|
`INSERT INTO users (id, username, password_hash) VALUES (?, ?, ?)`,
|
|
userID, username, passwordHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return s.GetUserByID(ctx, userID)
|
|
}
|
|
|
|
// UpdatePassword updates a user's password
|
|
func (s *UserStore) UpdatePassword(ctx context.Context, userID, newPassword string) error {
|
|
passwordHash, err := HashPassword(newPassword)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = s.DB.ExecContext(ctx,
|
|
`UPDATE users SET password_hash = ? WHERE id = ?`,
|
|
passwordHash, userID)
|
|
return err
|
|
}
|
|
|
|
// Authenticate verifies username and password
|
|
func (s *UserStore) Authenticate(ctx context.Context, username, password string) (*User, error) {
|
|
user, err := s.GetUserByUsername(ctx, username)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
valid, err := VerifyPassword(password, user.PasswordHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !valid {
|
|
return nil, errors.New("invalid password")
|
|
}
|
|
|
|
return user, nil
|
|
}
|