Files
zeromesh/internal/service/auth.go

114 lines
3.2 KiB
Go

package service
import (
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"golang.org/x/crypto/bcrypt"
"zeromesh/internal/config"
"zeromesh/internal/middleware"
"zeromesh/internal/model"
"zeromesh/internal/repository"
)
type AuthService struct {
userRepo *repository.UserRepo
jwtCfg config.JWTConfig
}
func NewAuthService(userRepo *repository.UserRepo, jwtCfg config.JWTConfig) *AuthService {
return &AuthService{userRepo: userRepo, jwtCfg: jwtCfg}
}
func (s *AuthService) Login(username, password string) (string, *model.User, error) {
user, err := s.userRepo.FindByUsername(username)
if err != nil {
return "", nil, errors.New("invalid credentials")
}
// Migrate old SHA256 hashes to bcrypt on login
if len(user.Password) < 4 || user.Password[:4] != "$2a$" {
if sha256Hash(password) != user.Password {
return "", nil, errors.New("invalid credentials")
}
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", nil, fmt.Errorf("migrate password: %w", err)
}
user.Password = string(hashed)
_ = s.userRepo.UpdatePassword(user.ID, user.Password)
} else if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
return "", nil, errors.New("invalid credentials")
}
token, err := middleware.GenerateToken(s.jwtCfg.Secret, user.ID, user.Username, user.Role, s.jwtCfg.ExpireHours)
if err != nil {
return "", nil, fmt.Errorf("generate token: %w", err)
}
user.Token = token
return token, user, nil
}
func (s *AuthService) Register(username, password string) (*model.User, string, error) {
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return nil, "", fmt.Errorf("hash password: %w", err)
}
user := &model.User{
Username: username,
Password: string(hashed),
Role: "user",
}
if err := s.userRepo.Create(user); err != nil {
return nil, "", err
}
token, err := middleware.GenerateToken(s.jwtCfg.Secret, user.ID, user.Username, user.Role, s.jwtCfg.ExpireHours)
if err != nil {
return nil, "", fmt.Errorf("generate token: %w", err)
}
user.Token = token
return user, token, nil
}
func (s *AuthService) InitAdmin(username, password string) (*model.User, string, error) {
count, err := s.userRepo.Count()
if err != nil {
return nil, "", err
}
if count > 0 {
return nil, "", fmt.Errorf("admin already exists")
}
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return nil, "", fmt.Errorf("hash password: %w", err)
}
user := &model.User{
Username: username,
Password: string(hashed),
Role: "admin",
QuotaNetworks: -1,
QuotaNodes: -1,
}
if err := s.userRepo.Create(user); err != nil {
return nil, "", err
}
token, err := middleware.GenerateToken(s.jwtCfg.Secret, user.ID, user.Username, user.Role, s.jwtCfg.ExpireHours)
if err != nil {
return nil, "", fmt.Errorf("generate token: %w", err)
}
user.Token = token
return user, token, nil
}
func sha256Hash(password string) string {
h := sha256.Sum256([]byte(password))
return hex.EncodeToString(h[:])
}
func (s *AuthService) CheckAdmin() (bool, error) {
return s.userRepo.HasAdmin()
}