forgejo-tickets/internal/auth/auth.go

123 lines
2.9 KiB
Go

package auth
import (
"context"
"fmt"
"net/http"
"github.com/google/uuid"
"github.com/mattnite/forgejo-tickets/internal/email"
"github.com/mattnite/forgejo-tickets/internal/models"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
type Service struct {
db *gorm.DB
store *PGStore
email *email.Client
}
func NewService(db *gorm.DB, store *PGStore, emailClient *email.Client) *Service {
return &Service{
db: db,
store: store,
email: emailClient,
}
}
func (s *Service) Register(ctx context.Context, emailAddr, password, name string) (*models.User, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return nil, fmt.Errorf("hash password: %w", err)
}
hashStr := string(hash)
user := models.User{
Email: emailAddr,
PasswordHash: &hashStr,
Name: name,
EmailVerified: false,
}
if err := s.db.WithContext(ctx).Create(&user).Error; err != nil {
return nil, fmt.Errorf("create user: %w", err)
}
return &user, nil
}
func (s *Service) Login(ctx context.Context, emailAddr, password string) (*models.User, error) {
var user models.User
if err := s.db.WithContext(ctx).Where("email = ?", emailAddr).First(&user).Error; err != nil {
return nil, fmt.Errorf("invalid email or password")
}
if user.PasswordHash == nil {
return nil, fmt.Errorf("this account uses social login")
}
if err := bcrypt.CompareHashAndPassword([]byte(*user.PasswordHash), []byte(password)); err != nil {
return nil, fmt.Errorf("invalid email or password")
}
if !user.EmailVerified {
return nil, fmt.Errorf("please verify your email before logging in")
}
if !user.Approved {
return nil, fmt.Errorf("your account is pending admin approval")
}
return &user, nil
}
func (s *Service) CreateSession(r *http.Request, w http.ResponseWriter, userID uuid.UUID) error {
session, err := s.store.Get(r, sessionCookieName)
if err != nil {
session, err = s.store.New(r, sessionCookieName)
if err != nil {
return err
}
}
session.Values["user_id"] = userID.String()
return s.store.Save(r, w, session)
}
func (s *Service) DestroySession(r *http.Request, w http.ResponseWriter) error {
session, err := s.store.Get(r, sessionCookieName)
if err != nil {
return nil
}
session.Options.MaxAge = -1
return s.store.Save(r, w, session)
}
func (s *Service) CreateUserWithPassword(ctx context.Context, emailAddr, password, name string, verified bool, approved bool) (*models.User, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return nil, fmt.Errorf("hash password: %w", err)
}
hashStr := string(hash)
user := models.User{
Email: emailAddr,
PasswordHash: &hashStr,
Name: name,
EmailVerified: verified,
Approved: approved,
}
if err := s.db.WithContext(ctx).Create(&user).Error; err != nil {
return nil, fmt.Errorf("create user: %w", err)
}
return &user, nil
}
func (s *Service) DB() *gorm.DB {
return s.db
}