Files
phishingclub/backend/service/install.go
2025-08-21 16:14:09 +02:00

131 lines
3.7 KiB
Go

package service
import (
"context"
"github.com/go-errors/errors"
"github.com/google/uuid"
"github.com/oapi-codegen/nullable"
"github.com/phishingclub/phishingclub/data"
"github.com/phishingclub/phishingclub/errs"
"github.com/phishingclub/phishingclub/model"
"github.com/phishingclub/phishingclub/password"
"github.com/phishingclub/phishingclub/repository"
"github.com/phishingclub/phishingclub/vo"
"gorm.io/gorm"
)
// InstallSetup sets up the intial administrator account
// this is called by the installation process and is not part of the normal application flow
// it must not use services that require authentication or be used by any other part of the application
type InstallSetup struct {
Common
UserRepository *repository.User
RoleRepository *repository.Role
CompanyRepository *repository.Company
PasswordHasher *password.Argon2Hasher
}
// SetupAccounts sets up the accounts needed for the system to function
func (s *InstallSetup) SetupAccounts(
ctx context.Context,
) (*model.User, *vo.ReasonableLengthPassword, error) {
user, password, err := s.setupInitialAdministratorAccount(ctx)
if err != nil {
s.Logger.Errorw("failed to setup the initial administrator account", "error", err)
return nil, nil, errs.Wrap(err)
}
return user, password, nil
}
// setupInitialAdministratorAccount sets up the initial administrator account
func (s *InstallSetup) setupInitialAdministratorAccount(
ctx context.Context,
) (*model.User, *vo.ReasonableLengthPassword, error) {
username := vo.NewUsernameMust(data.DefaultSacrificalAccountUsername)
nullableUsername := nullable.NewNullableWithValue(*username)
// get the admin user if it already exists
// this could happend if the installation was started, but not completed
adminUser, err := s.UserRepository.GetByUsername(
ctx,
username,
&repository.UserOption{
WithRole: true,
WithCompany: true,
},
)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil, errs.Wrap(err)
}
password, err := vo.NewReasonableLengthPasswordGenerated()
if err != nil {
return nil, nil, errs.Wrap(err)
}
// if the admin account does not exist, create it
if adminUser == nil {
adminRole, err := s.RoleRepository.GetByName(
ctx,
data.RoleSuperAdministrator,
)
if err != nil {
return nil, nil, errs.Wrap(err)
}
email := nullable.NewNullableWithValue(*vo.NewEmailMust(data.DefaultSacrificalAccountEmail))
fullname := nullable.NewNullableWithValue(*vo.NewUserFullnameMust(data.DefaultSacrificalAccountName))
tmpAdminID := nullable.NewNullableWithValue(uuid.New())
tmpAdmin := &model.User{
ID: tmpAdminID,
Name: fullname,
Username: nullableUsername,
Email: email,
RoleID: nullable.NewNullableWithValue(adminRole.ID),
}
hash, err := s.PasswordHasher.Hash(password.String())
if err != nil {
return nil, nil, errs.Wrap(err)
}
newUserID, err := s.UserRepository.Insert(
ctx,
tmpAdmin,
hash,
"",
)
if err != nil {
return nil, nil, errs.Wrap(err)
}
adminUser, err = s.UserRepository.GetByID(
ctx,
newUserID,
&repository.UserOption{
WithRole: true,
WithCompany: false,
},
)
if err != nil {
return nil, nil, errs.Wrap(err)
}
} else {
username, err := vo.NewUsername(data.DefaultSacrificalAccountUsername)
if err != nil {
return nil, nil, errs.Wrap(err)
}
// if the admin account exists, update the password
hash, err := s.PasswordHasher.Hash(password.String())
if err != nil {
return nil, nil, errs.Wrap(err)
}
err = s.UserRepository.UpdatePasswordHashByUsername(
ctx,
username,
hash,
)
if err != nil {
return nil, nil, errs.Wrap(err)
}
return adminUser, password, nil
}
return adminUser, password, nil
}