build multi tenant
This commit is contained in:
229
auth/service.go
Normal file
229
auth/service.go
Normal file
@@ -0,0 +1,229 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"github.com/ajaxray/geek-life/model"
|
||||
"github.com/ajaxray/geek-life/repository"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidCredentials = errors.New("invalid credentials")
|
||||
ErrUserExists = errors.New("user already exists")
|
||||
ErrTenantExists = errors.New("tenant already exists")
|
||||
)
|
||||
|
||||
// AuthService handles authentication operations
|
||||
type AuthService struct {
|
||||
userRepo repository.UserRepository
|
||||
tenantRepo repository.TenantRepository
|
||||
sessionRepo repository.SessionRepository
|
||||
sessionDuration int // in hours
|
||||
}
|
||||
|
||||
// NewAuthService creates a new authentication service
|
||||
func NewAuthService(userRepo repository.UserRepository, tenantRepo repository.TenantRepository, sessionRepo repository.SessionRepository, sessionDuration int) *AuthService {
|
||||
return &AuthService{
|
||||
userRepo: userRepo,
|
||||
tenantRepo: tenantRepo,
|
||||
sessionRepo: sessionRepo,
|
||||
sessionDuration: sessionDuration,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterTenant creates a new tenant with an admin user
|
||||
func (s *AuthService) RegisterTenant(tenantName, username, email, password string) (*model.UserContext, string, error) {
|
||||
// Check if tenant already exists
|
||||
if _, err := s.tenantRepo.GetByName(tenantName); err == nil {
|
||||
return nil, "", ErrTenantExists
|
||||
}
|
||||
|
||||
// Create tenant
|
||||
tenant, err := s.tenantRepo.Create(tenantName)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Check if user already exists in this tenant
|
||||
if _, err := s.userRepo.GetByUsername(tenant.ID, username); err == nil {
|
||||
return nil, "", ErrUserExists
|
||||
}
|
||||
if _, err := s.userRepo.GetByEmail(tenant.ID, email); err == nil {
|
||||
return nil, "", ErrUserExists
|
||||
}
|
||||
|
||||
// Hash password
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Create user
|
||||
user, err := s.userRepo.Create(tenant.ID, username, email, string(hashedPassword))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Create session
|
||||
token, err := s.generateToken()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
expiresAt := time.Now().Add(time.Duration(s.sessionDuration) * time.Hour).Unix()
|
||||
_, err = s.sessionRepo.Create(user.ID, token, expiresAt)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
ctx := &model.UserContext{
|
||||
User: user,
|
||||
Tenant: tenant,
|
||||
}
|
||||
|
||||
return ctx, token, nil
|
||||
}
|
||||
|
||||
// RegisterUser creates a new user in an existing tenant
|
||||
func (s *AuthService) RegisterUser(tenantName, username, email, password string) (*model.UserContext, string, error) {
|
||||
// Get tenant
|
||||
tenant, err := s.tenantRepo.GetByName(tenantName)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Check if user already exists in this tenant
|
||||
if _, err := s.userRepo.GetByUsername(tenant.ID, username); err == nil {
|
||||
return nil, "", ErrUserExists
|
||||
}
|
||||
if _, err := s.userRepo.GetByEmail(tenant.ID, email); err == nil {
|
||||
return nil, "", ErrUserExists
|
||||
}
|
||||
|
||||
// Hash password
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Create user
|
||||
user, err := s.userRepo.Create(tenant.ID, username, email, string(hashedPassword))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Create session
|
||||
token, err := s.generateToken()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
expiresAt := time.Now().Add(time.Duration(s.sessionDuration) * time.Hour).Unix()
|
||||
_, err = s.sessionRepo.Create(user.ID, token, expiresAt)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
ctx := &model.UserContext{
|
||||
User: user,
|
||||
Tenant: tenant,
|
||||
}
|
||||
|
||||
return ctx, token, nil
|
||||
}
|
||||
|
||||
// Login authenticates a user and returns a session token
|
||||
func (s *AuthService) Login(tenantName, username, password string) (*model.UserContext, string, error) {
|
||||
// Get tenant
|
||||
tenant, err := s.tenantRepo.GetByName(tenantName)
|
||||
if err != nil {
|
||||
return nil, "", ErrInvalidCredentials
|
||||
}
|
||||
|
||||
// Get user by username or email
|
||||
var user *model.User
|
||||
user, err = s.userRepo.GetByUsername(tenant.ID, username)
|
||||
if err != nil {
|
||||
// Try by email
|
||||
user, err = s.userRepo.GetByEmail(tenant.ID, username)
|
||||
if err != nil {
|
||||
return nil, "", ErrInvalidCredentials
|
||||
}
|
||||
}
|
||||
|
||||
// Verify password
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password))
|
||||
if err != nil {
|
||||
return nil, "", ErrInvalidCredentials
|
||||
}
|
||||
|
||||
// Create session
|
||||
token, err := s.generateToken()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
expiresAt := time.Now().Add(time.Duration(s.sessionDuration) * time.Hour).Unix()
|
||||
_, err = s.sessionRepo.Create(user.ID, token, expiresAt)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
ctx := &model.UserContext{
|
||||
User: user,
|
||||
Tenant: tenant,
|
||||
}
|
||||
|
||||
return ctx, token, nil
|
||||
}
|
||||
|
||||
// ValidateSession validates a session token and returns user context
|
||||
func (s *AuthService) ValidateSession(token string) (*model.UserContext, error) {
|
||||
// Get session
|
||||
session, err := s.sessionRepo.GetByToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get user
|
||||
user, err := s.userRepo.GetByID(session.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get tenant
|
||||
tenant, err := s.tenantRepo.GetByID(user.TenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx := &model.UserContext{
|
||||
User: user,
|
||||
Tenant: tenant,
|
||||
}
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// Logout invalidates a session
|
||||
func (s *AuthService) Logout(token string) error {
|
||||
session, err := s.sessionRepo.GetByToken(token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.sessionRepo.Delete(session)
|
||||
}
|
||||
|
||||
// generateToken generates a random session token
|
||||
func (s *AuthService) generateToken() (string, error) {
|
||||
bytes := make([]byte, 32)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(bytes), nil
|
||||
}
|
||||
Reference in New Issue
Block a user