Files
wg-portal/internal/app/api/v0/backend/user_service.go
2025-10-15 21:11:40 +02:00

124 lines
3.9 KiB
Go

package backend
import (
"context"
"fmt"
"strings"
"github.com/h44z/wg-portal/internal/config"
"github.com/h44z/wg-portal/internal/domain"
)
// region dependencies
type UserServiceUserManager interface {
GetUser(ctx context.Context, id domain.UserIdentifier) (*domain.User, error)
GetAllUsers(ctx context.Context) ([]domain.User, error)
CreateUser(ctx context.Context, user *domain.User) (*domain.User, error)
UpdateUser(ctx context.Context, user *domain.User) (*domain.User, error)
DeleteUser(ctx context.Context, id domain.UserIdentifier) error
ActivateApi(ctx context.Context, id domain.UserIdentifier) (*domain.User, error)
DeactivateApi(ctx context.Context, id domain.UserIdentifier) (*domain.User, error)
}
type UserServiceWireGuardManager interface {
GetUserPeers(ctx context.Context, id domain.UserIdentifier) ([]domain.Peer, error)
GetUserInterfaces(ctx context.Context, _ domain.UserIdentifier) ([]domain.Interface, error)
GetUserPeerStats(ctx context.Context, id domain.UserIdentifier) ([]domain.PeerStatus, error)
}
// endregion dependencies
type UserService struct {
cfg *config.Config
users UserServiceUserManager
wg UserServiceWireGuardManager
}
func NewUserService(cfg *config.Config, users UserServiceUserManager, wg UserServiceWireGuardManager) *UserService {
return &UserService{
cfg: cfg,
users: users,
wg: wg,
}
}
func (u UserService) GetUser(ctx context.Context, id domain.UserIdentifier) (*domain.User, error) {
return u.users.GetUser(ctx, id)
}
func (u UserService) GetAllUsers(ctx context.Context) ([]domain.User, error) {
return u.users.GetAllUsers(ctx)
}
func (u UserService) UpdateUser(ctx context.Context, user *domain.User) (*domain.User, error) {
return u.users.UpdateUser(ctx, user)
}
func (u UserService) CreateUser(ctx context.Context, user *domain.User) (*domain.User, error) {
return u.users.CreateUser(ctx, user)
}
func (u UserService) DeleteUser(ctx context.Context, id domain.UserIdentifier) error {
return u.users.DeleteUser(ctx, id)
}
func (u UserService) ActivateApi(ctx context.Context, id domain.UserIdentifier) (*domain.User, error) {
return u.users.ActivateApi(ctx, id)
}
func (u UserService) DeactivateApi(ctx context.Context, id domain.UserIdentifier) (*domain.User, error) {
return u.users.DeactivateApi(ctx, id)
}
func (u UserService) ChangePassword(ctx context.Context, id domain.UserIdentifier, oldPassword, newPassword string) (*domain.User, error) {
oldPassword = strings.TrimSpace(oldPassword)
newPassword = strings.TrimSpace(newPassword)
if newPassword == "" {
return nil, fmt.Errorf("new password must not be empty")
}
// ensure that the new password is different from the old one
if oldPassword == newPassword {
return nil, fmt.Errorf("new password must be different from the old one")
}
user, err := u.users.GetUser(ctx, id)
if err != nil {
return nil, fmt.Errorf("failed to get user: %w", err)
}
// ensure that the user uses the database backend; otherwise we can't change the password
if user.Source != domain.UserSourceDatabase {
return nil, fmt.Errorf("user source %s does not support password changes", user.Source)
}
// validate old password
if user.CheckPassword(oldPassword) != nil {
return nil, fmt.Errorf("current password is invalid")
}
user.Password = domain.PrivateString(newPassword)
// ensure that the new password is strong enough
if err := user.HasWeakPassword(u.cfg.Auth.MinPasswordLength); err != nil {
return nil, err
}
return u.users.UpdateUser(ctx, user)
}
func (u UserService) GetUserPeers(ctx context.Context, id domain.UserIdentifier) ([]domain.Peer, error) {
return u.wg.GetUserPeers(ctx, id)
}
func (u UserService) GetUserPeerStats(ctx context.Context, id domain.UserIdentifier) ([]domain.PeerStatus, error) {
return u.wg.GetUserPeerStats(ctx, id)
}
func (u UserService) GetUserInterfaces(ctx context.Context, id domain.UserIdentifier) ([]domain.Interface, error) {
return u.wg.GetUserInterfaces(ctx, id)
}